I started reading King, on writing. I jumped to the chapter on having a toolbox. I was only concerned with his method of writing. I thought there may be some very good advice that writers have concerning the writing process, especially the iterative writing process that takes very sloppy first drafts through a process of refinement to final product.
He talks about your toolbox being portable. He says your toolbox must have levels. He starts by advising us to put common tools on top. He asks what is the most common tool? This provokes me to ask what is a developer’s most common tool? Some ideas spring to mind immediately. Probably a text editor or IDE? Is it the compiler? When King mentions his most common tool, it’s ‘vocabulary’. Is a developer’s most common tool the knowledge of the language? Is it algorithms? Is it code patterns/architecture? Is it platform or library knowledge? Is it computer science basics like computers and networking?
“Put your vocabulary on the top shelf of your toolbox, and don’t make any conscious effort to improve it. (You’ll be doing that as you read, of course…)” This quote from the book emphasizes reading to improve vocabulary. Similarly, developers read code to improve their coding, but what code? Most code isn’t good code. It’s ‘working’ code that satisfies the compiler, but for a human, it’s cluttered, unorganized, and hodgepodge. There may have been an attempt at structure in the beginning, but after a while, the whole thing deteriorates into a garbled mess. Is this the source of good ‘reading’ for software developers?
“Use the first word that comes to your mind, if it is appropriate and colorful.”
To use the right language in code that’s appropriate. That’s why naming is so important in coding. We use the first thing that comes to mind, and it’s usually not appropriate nor colorful. I started using inappropriate names for variables like ‘x’ because I often don’t determine the correct word for a variable until after I’ve assigned it and maybe even refactored it. Then the purpose of the variable becomes apparent.
His second most common tool is grammar. How would grammar relate to programming? Programming structure? What are variables, conditions, loops? What is a clean implementation? I recently watched a video of coding advice, and one tip was reducing nested ifs. But in the example, it was more like defensive coding or fail-fast coding—get the exceptions to the norm out of the way first, then create the true logic intended for the happy path. That was somewhat of a ‘coding grammatical rule.’ What would be other such ‘grammatical’ devices used in coding?
Say you don’t fully understand grammar (English language grammar) and you’re still writing—how do you know if you’re doing it well? Similarly, if you don’t know the ‘grammar’ of coding, how do you know if you’re doing well? Does anyone know the ‘rules of coding grammar’? Are there any rules of coding grammar? Most of it seems to be opinion. I watched a rant on YouTube where Linus Torvalds was upset with some code that combined two 16-bit words into a 32-bit word. Which was the higher byte? (This made me think of the need for named parameters in calls like Assert.AreEqual(expected: x, actual: y)
.) He said (a << 16) + b
would suffice, and there was no need for another utility function. Maybe that’s just style? Is grammar something the compiler will tell you about? If so, then a programmer doesn’t need to be concerned so much with grammar if the compiler won’t accept the code.
Active vs. passive verbs? Is there an analogy to coding? There’s code that does something, and then there’s code added for other reasons—logging, caching, validating, and many ancillary tasks needed in a piece of code before you get to the actual meat of the task. Authentication, permission, parsing. I’ve always thought that all coding is just a matter of taking data in one form and turning it into another at the end of the day. (King might not approve of such an overused, cliché, stock phrase.) Active code would be the heart of what the developer wants to happen, the use-case, whereas the passive code would be the code needed for the application to behave well.
King advises avoiding ‘passive’ verbs. Would this be akin to aspect programming patterns? Isolate those ‘passive’ coding needs and use them in a ‘decorative’ style instead of procedurally interspersing them in your code. It clutters the meaning of what you’re doing. There’s so much context to every method call today. You need to be concerned with all the ancillary things while focusing on the true behavior. Maybe if we think of our public entry methods as the API to the user of our code, then we can consider all the things needed to accomplish the caller’s needs—that black box of encapsulation—but do it in an organized manner.
Think about context. There’s always context building or capturing. You must pull in all the needed context from various places to perform the task. You may say, what happens when certain paths of execution need more context? Wouldn’t you need to capture more context later on in your procedure? This could lead down a rabbit hole of using strategy patterns or functional programming techniques. Even so, if you consider that certain data may be needed later, try to consider all the needed data up front, even if you have to hide that data behind lazy loading for when it’s needed. What I’m trying to say is, load your context first. Do all the context-loading code (or lazy call code) up front. Now that data is available for whatever the procedure needs in its journey to fulfill the caller’s need. Have a ‘context’ object with static data and lazy-load data already constructed for use.
Then there’s validation. All user input needs to be validated before commencing to fulfill the core request of the user or API caller. This must be done before any true user needs happen. If you have your context data, then your validation can take that data and determine whether the request can continue or error out. Again, you may need to make these decisions further down the procedural track, but you can use lazy validation.
There are other concerns like logging, caching, and parsing. Things like logging and caching should be decorative. We shouldn’t be putting logging statements everywhere, nor should we reinvent the wheel on caching every time we need it. Using the decorator pattern or aspect-oriented programming can reduce or hopefully eliminate the need for this type of code to creep into your true user-needed code.
Then there’s the question of which code gets executed based on what scenarios. All code is determined by the state of your context. When you’re in Wisconsin, you extract x% of payroll income tax, but when you’re in Washington, you don’t extract any payroll income tax. But maybe in Wisconsin (I know nothing about payroll, mostly), on special holidays, you don’t charge payroll income tax but give a bonus instead. State machines are meant to handle these scenarios, but not many programmers are versed in state machines, so you see nested if statements everywhere. These nested if statements are necessary because the context wasn’t gathered initially, and validation wasn’t done up front. If your context is captured (even lazily) and your validation is done up front based on that context, then all you need to do is feed your context into the state machine, and it’s the job of the state machine to give you an implementation to execute against the parameters sent in.
You may have one state machine for Washington, with no payroll income tax, which is a null pattern. You may have multiple state machines for Wisconsin for whatever various rules they impose. The state machine will take the context and determine the most appropriate implementation to execute. If Wisconsin laws change, you go to the Wisconsin implementations and change them. You don’t dig through 10,000 lines of code to find all the places that need changes.
The most important thing to keep in mind with state machines is to order your implementation selections from most complex to least. The situations with the most rules go first; those with the least, the null pattern, or the default thing to do, go last. Order is critical in this matter.
Also consider that when your state machine begins to choose what implementation to give the caller, there will be short circuits in every predicate that determines what it chooses. Imagine each implementation behind an ordered list of implementations and predicates, then it’s just a collection. You iterate the collection until you find the appropriate implementation you require—state: Wisconsin, holiday: today
, etc. The most complex cases come first. When the predicate is evaluated using exclusive ANDs (&&
) and ORs (||
), if the most important predicate (something you decide when programming, like the state in our example, which is readily available statically and needed for 99% of the use cases), then you can skip the predicates that don’t comply and never touch the lazy data needed for those use cases.