The Beginning of My Relationship with The Art of UNIX Programming (and Eric S. Redmond) (though neither the topic nor the author know that we started this relationship)

Kate Norton
7 min readJul 23, 2020

The Art of UNIX Programming is a book by Eric S. Redmond. I think of myself as beginning a relationship with UNIX Programming because I’m not an authority on the man or book, but I’m falling in love, and you’re here to hear me, not them, and see me gush, not teach. So I’ll on with the gushing, and we can do away with me pretending to be any kind of authority.

The Art of UNIX Programming, as far as I can tell, is to a programmer what an an acting style would be to an actress. A Method actress, for example, might use her own memories and experiences to access her emotions, while a Chekhov actress might use her imagination to generate emotions. An audience member usually does not know what acting style an actress is using but it nevertheless affects her performance and process.

In just this way, an app user won’t usually know what programming style a developer uses. A developer using the Art of UNIX Programming to help her make choices might be doing things that change the process and product without the user knowing. Whatever reason an actress or an engineer adheres to a process, the results will change.

I started to fall for UNIX Programming methodology when a friend who is in the Grace Hopper program with me recommended it to me. I liked what I learned, and I bought the book.

Like any good how-to book, it has an expressive, opinionated, organized list of things that you should do if you are going to start a relationship with the book’s ideas. The book lists the ideas thusly (click on the links to read more detailed explanations for each idea — all ideas are bullet points are from Eric Redmond):

  1. Rule of Modularity: Write simple parts connected by clean interfaces.
  2. Rule of Clarity: Clarity is better than cleverness.
  3. Rule of Composition: Design programs to be connected with other programs.
  4. Rule of Separation: Separate policy from mechanism; separate interfaces from engines.
  5. Rule of Simplicity: Design for simplicity; add complexity only where you must.
  6. Rule of Parsimony: Write a big program only when it is clear by demonstration that nothing else will do.
  7. Rule of Transparency: Design for visibility to make inspection and debugging easier.
  8. Rule of Robustness: Robustness is the child of transparency and simplicity.
  9. Rule of Representation: Fold knowledge into data, so program logic can be stupid and robust.
  10. Rule of Least Surprise: In interface design, always do the least surprising thing.
  11. Rule of Silence: When a program has nothing surprising to say, it should say nothing.
  12. Rule of Repair: Repair what you can — but when you must fail, fail noisily and as soon as possible.
  13. Rule of Economy: Programmer time is expensive; conserve it in preference to machine time.
  14. Rule of Generation: Avoid hand-hacking; write programs to write programs when you can.
  15. Rule of Optimization: Prototype before polishing. Get it working before you optimize it.
  16. Rule of Diversity: Distrust all claims for one true way.
  17. Rule of Extensibility: Design for the future, because it will be here sooner than you think.

Sexy.

I’m applying these ideas as best I can to a project I am working on. I’m working on an app to display NYPD force data in a clean and accessible way. I’m using Express.js and to get the data from my PostgreSQL database and into my optimized front-end React and Victory application. I had to make a decision about how best to handle 16 x 5 plus a few permutations that I would need to be able to serve the data properly:

//HERE'S A SKETCH OF THE PERMUTATIONS THAT I USED TO HELP ME THINK
/api/graphData/subjectInjuries/allYear/allQuarter/on/allCommand is #3 on the /subjectInjuries route
//year, quarter, duty, command
//1 a a a a
//2 a a a :
//3 a a : a
//6 a a : :
//4 a : a a
//7 a : a :
//8 a : : a
//12 a : : :
//5 : a a a
//10 : a a :
//9 : a : a
//13 : a : :
//11 : : a a
//14 : : a :
//15 : : : a
//16 : : : :
//I LOVE TO DO SKETCHES OF THINGS THAT HELP ME THINK

So, I was faced with a dilemma, do I do 52 routes in a pattern that I can see clearly thought it is very repetitive or do I try to do a elegant, magical, refined, impressive sequence of conditional logic?

//HERE I GO! I'M GOING TO GET EVERY PERMUTATION IN ONE ROUTE!
//WATCH THE MAGIC, SEE ME SPIN, I AM FIERCE!
router.get('/', async function (req, res, next) {
const year = req.query.year ? req.query.year : false
const quarter = req.query.quarter ? req.query.quarter : false
const duty = req.query.duty ? req.query.duty : false
const command = req.query.duty ? req.query.duty : false
const officerInjuryData = await OfficerInjury.findAll({
let timeFrame
if(year && quarter){
timeFrame = await TimeFrame.findAll({
where: {quarter: req.params.quarter, year: req.params.year},
})
else if (year && !quarter) {
timeFrame = await TimeFrame.findAll({
where: {year: req.params.year},
})
else if (!year && quarter) {
timeFrame = await TimeFrame.findAll({
where: {year: req.params.quarter},
})
else if (!year && !quarter) {

//then the we want all the time and now we have to do some kind of logic that tells the program that if in this case timeFrame is not defined then we want all the times, and we don't do a search for the injuries based upon time, so in this else if we have to do ALL the logic for the conditions of if there is a duty or not or all in this else if, but outside of this, everyone of those above need to run the other code because they do have the timeFrame? Hmmm...this is starting to become unclear, and I'm going to have more ifs inside of ifs no matter how I do this and if I want to make it pretty, I'm going to have to use the ? and : in order to not embarrass myself...
}
}
}
})
})
//HMMM. MAYBE I SHOULD DO IT SOME OTHER WAY?

But if I used Express.js to act like a very organized, highly-readable path system, my code might be a lot longer, but it might be overall a much better idea!

//4  a      :      a       a  <--All Years, Specific Quarter, All Injuries, All Duty, All Command
router.get('/allYear/:quarter/allDuty/allCommand/', async function (
req,
res,
next
) {
try {
let timeFrame = await TimeFrame.findAll({
where: {quarter: req.params.quarter},
})
let timeFrameIds = timeFrame.map((item) => item.id)
let officerInjuriesData = []
for (let i = 0; i < timeFrameIds.length; i++) {
officerInjuriesData = [
...officerInjuriesData,
...(await OfficerInjury.findAll({
where: {timeFrameId: timeFrameIds[i]},
attributes: ['onDuty', 'offDuty'],
include: [
{model: Command, attributes: ['commandName']},
{model: InjuryType, attributes: ['type']},
{model: TimeFrame, attributes: ['year', 'quarter']},
],
})),
]
}
let subjectInjuriesData = []
for (let i = 0; i < timeFrameIds.length; i++) {
subjectInjuriesData = [
...subjectInjuriesData,
...(await SubjectInjury.findAll({
where: {timeFrameId: timeFrameIds[i]},
attributes: ['onDuty', 'offDuty'],
include: [
{model: Command, attributes: ['commandName']},
{model: InjuryType, attributes: ['type']},
{model: TimeFrame, attributes: ['year', 'quarter']},
],
})),
]
}
res.json({
officerData: officerInjuriesData,
subjectData: subjectInjuriesData,
})
console.log(
'api/graphData/allInjury/allYear/:quarter/allDuty/allCommand/ route'
)
} catch (error) {
next(error)
}
})

As you can see, that one route is not simple. The looping to get the injuries data based upon an instance in which a user wants all the quarters for every year (quarter 1 for all years of data) would have been required in both approaches. The second approach allowed me to think clearly and get it done. The first approach twisted my mind up 5 minutes into trying to write the logic, and I was nowhere the place where I had to consider that user story.

Rules 2, 3, 5, 7, 8, 9, 13, 15, 16, 17 apply here. The second method was preferable because: 2) it’s clearer; 3) the data needs to be clearly formatted in the exact same way no matter what permutation the app calls for; 5) it’s simpler; 7) each permutation is easily and clearly labelled, which makes it not only easier to debug, but easier to write; 8) each route is only doing one thing, and it is always doing that one thing, so though there are many routes, each is robust in its single focus; 9) everything in all those routes is simply about getting the specific data wanted and serving just that; 13) it is faster for me to write and debug than the other approach; 15) it is a prototype and seeing it come alive first is the priority; 16) no way is the best, so going in one way is just a way and deliberating on what is the “best” approach is flawed; and 17) if the data provided in the future by the NYPD changes, changing this code will be easy, because I can just write additional routes for the new data, and the old data can still use those old routes, but a complex string of logic conditions might not be so easily amended.

Computer drawing of a happy woman at a club.
A engineer that sees her application working and isn’t stressed out because she used The Art of UNIX Programming.

I see the value in these approaches.

Basically, I think, they help make me not just a good engineer, but also a good engineer-citizen.

By the way, if you want to check out my application and see how the NYPD is using force in NYC, please do!

--

--