Why Programming skills are Life skills
Shane Ayers - SPHR, SCP, MS
Systems, Processes, Automation, Categorization and Exploration.
When most people think about programming, they think about people creating incomprehensible lines of code that perform minor feats of magic on computers and other digital devices. While this is what programmers do, it is not the essence of programming.
When programmers think about programming, they think about thinking. They think about how to solve the immediate problem, yes, and a bad programmer does that and stops there. A good programmer thinks about what problems the solution causes and how to prevent them. A good programmer thinks not only about how to solve the problem but how to do so with the minimum amount of work, now and in the future, for themselves and others.
In programming, a lot of thought and discussion space is devoted to principles for reducing rework and technical debt. Rework - as a noun, rework means doing something a second time that you considered complete the first time. Contrast this with cyclical or repetitive tasks which you know you will need to do again due to their nature. Technical debt - this is the second-order work that you need to do after having done the initial task. Commenting code and refactoring code are examples of technical debt which is accrued as we solve problems using programming.
What does this have to do with life?
In programming, we have different languages, frameworks, and tech stacks. We are using different types of data, on different operating systems, running on different hardware, in different business contexts, and under different constraints. All that is to say that while there is a single term ('programming') to describe this, there is no unified or average programming scenario. It is an entire universe of idiosyncratic situations. As a result of that, programming principles are situationally agnostic. They work with any language. They work on any device. They work in any situation. They are everything-tools by necessity. They are process improvement technology for pure abstractions. Thus, we can apply them even when solving problems that have no digital components.
D.R.Y.
In my experience, one of the most fundamental aspects of programming is encoded by the acronym DRY. Don't Repeat Yourself. If you have to do something twice, you should go ahead and make sure you never have to do it again. In programming the way that this works is that if you write code for the same process twice, that code should be turned into a function (think of this as a little digital worker that does a particular task), so you never have to rewrite that code. Taking it up one level, it also means that if you wrote code to do something, like convert a particular filetype, you should not have to write that code again. If you've solved a problem, you should not have to solve it again.
In my work life, DRY has guided a lot of my thinking about things that aren't technically related. Recently, I had an experience that many would consider a good reason to slap their own forehead in frustration. I had to redo an audit that I had already done to understand why I reached the conclusion I reached the first time. In other words, the problem had not changed at all. It was not a new instance of the same problem. It was the same problem. I had already solved it, and I had to do every single bit of work that I did a second time. The solution in a case like this is simple; document everything. If I had documented the audit the first time, I could just refer to my notes, or have someone else refer to my notes, rather than having to perform the work again.
However, this is not the most egregious way that people tend to repeat themselves. The most frequent place that I see DRY errors is in process handoffs. This includes times when one person is handing work off to another person and also when someone resumes work that they started previously. What typically happens is that the receiving person has to do some (or all) of the original work again to get things to a place where they can perform the next steps. The amount of waste that occurs due to this is incalculable. While there are good solutions that we're going to cover shortly with the next framework, part of this is also communication. IT teams have gotten very good at this, logging each step of the process of resolving a ticket so that each new recipient knows what has happened in the past and what needs to happen next. Taken another layer up, this shows up as knowledgebases describing problems and their historical solutions to further prevent rework on longer time scales.
Another important way that this shows up is with frameworks. If you have already developed a way to do something that is generalizable and repeatable, you do not need to do that. Even better than that, if someone else has already developed that, you don't need to do it the first time. In other words, don't reinvent the wheel. Don't repeat other people either!
If you're going to have to, for example, do a certain type of audit over and over again as new cases requiring it come in, then it is a good idea to review your work the first time you do it and create a general workflow for where to look, what information to look for, how to find and represent the standards you're evaluating it against, how to perform the evaluation, and how to present or represent the findings. You should not be inventing this on the fly every single time you need to do this.
Furthermore, since many different activities will share common steps or requirements, it also means that even if there is only limited task overlap, you should not have to do everything from scratch. (A cautious reader may understand at this point that this entire article is actually a manifestation of this single point).
S.O.L.I.D. Design Framework
A good next step in our journey of programmifying everyday life is applying the SOLID framework, which is standard for good programmers across industries.
S
The S in SOLID stands for Single Responsibility. What that means is that a given piece of code should do one and only one thing. A good ostensive example of why it's needed is in troubleshooting. When code doesn't execute properly, and it has been designed using good principles, it tells us what broke. We have to figure out why. If you have one piece of code that does 20 things, how will you know which part didn't work? Imagine that you have a piece of code that only does 1 thing. How much longer does it take to look through the first piece of code versus the second? How much less energy is it to evaluate what might have happened in a piece of code that only does one thing? Multiply that difference by all of the instances of this that you will experience over the course of a career and you understand why this principle is so important.
The Single Responsibility principle is actually in ubiquitous use around the world all the time. It is so commonplace that we don't notice it. At a very high level, consider industries. Industries are groupings of organizations that do 'one thing'. Organizations in Higher Education, which is the industry I work in, educate adults on niche interests and fields. Education in general, which includes places like elementary schools, trade schools, and boot camps, educates more than just adults on more than just niche interests. This same split exists at the level of governance. A government is responsible for the activity in a specific geographically bounded land mass, or relating to a population meaningfully tied to it.
Moving down a level, you have individual organizations or individual government departments. While the whole of Higher Education provides the function of educating adults in niche fields, a specific institution may solely educate adults in how to be doctors. While the whole of a Government may provide the function of administrating activity within a country, a regional government may have a tighter geographic area, such as a state and city government, or a specific department may only deal with one type of problem, for example the department of labor or the IRS.
Down another level you have individual departments with an organization or types of roles. You have executives, who have a different fundamental function from managers, who have a different fundamental function from the employees who report to them. You have departments such as Human Resources or Security or Billing or Accounting which are tasked with performing a single specific function such as hiring people or securing the physical space or collecting monies or balancing transaction records.
However, what I find to be the most useful of the SRP is in thinking solely about an individual. How many hats are you trying to wear? How many things are you trying to be good at? How many things are you trying to do at the same time? And what impact does that have as opposed to doing a single thing at a time?
O
The O in SOLID stands for Open-Closed. In programming, this principle essentially discusses a philosophy on interacting with systems. It says that you should be able to change the functionality of a piece of code without actually changing that piece of code by extending that code. What does this mean? it means open for extension, closed for modification. If you have a function that does something and you'd like it to do more, you don't modify the code in the function. You write extensions to it, such as decorators. This is simultaneously a nod to the aphorism "If it ain't broke, don't fix it" and to the ever-true idea that "change is constant".
Let's summarize in a way that is more generally applicable. We must change some things to keep up with reality but we must not break things that already work.
领英推荐
Where do we see this principle already used in real life? This is often something observable in policy. Policies are, by their nature, a palimpsest. In maintaining a robust system of rules and if/then conditional statements, by necessity, policy-making individuals and bodies have converged on the optimal solution being adding and deleting rather than overwriting or rewriting as the default response. What does this mean? It means that you will notice that in law, in contracts, in policies, in statutes, in rules of all sorts, systemic inertia plays an outsized role. It is extremely difficult to get something changed by intention because it reduces the probability that you're going to break things elsewhere in the system through your modification. The way to go instead is to add something that substantively modifies the outcome without modifying the underlying rules (think the Constitution and the law).
Another place you'll see this is in planning and execution. The mission can be considered closed to modification but open to extension via time-based goals, and departmental missions that connect into but differ from it. A plan can be considered closed to modification but open to extension in the sense that we may inject additional steps without changing the overall direction, subgoals, etc.
L
The L in SOLID stands for Liskov Substitution. I have a personal aversion towards ideas named after people since they have no affordances for understanding, but here we are. It is difficult to explain Liskov Substitution without having some base knowledge of programming. Let's try it by example.
You have a car company and you have a car model, let's call it the Alpha. The Alpha is a base model. It is possible to create subtypes of Alpha. The Liskov Substitution Principle merely states that any subtype you create must be capable of replacing the base model. Any situation where the base model would be used, the subtype must be able to perform in the exact same way in those scenarios. So, let's say I have an Alpha Sport. The Alpha Sport should still be able to throttle down performance to that of the Alpha. If the Alpha can tow a shipping container, the Alpha Sport needs to be able to do that too. If the Alpha can automatically break and stop on a dime, the Sport needs to do that as well. This principle is recursion agnostic. What i mean is that if I have a subtype of a subtype, say the Alpha Sport Turbo S Class, it needs to be able to perform the same functions as the Alpha Sport Turbo, the Alpha Sport, and the Alpha.
In other words, when making subtypes, we can add but we cannot meaningfully subtract.
Where do we see this already in real life? Substitute teachers. A substitute teacher is a special subtype of teacher who does not teach daily and must be available to step in for any other teacher at any point in time and function in the same way that that teacher would (absent things like relationships with students, knowledge of specific lessons covered, and other non-generalizable and difficult to transmit information). More than that though, for the elementary school environment, it means that a math teacher should not only be able to teach math but should also be able to step in to teach English. At that level, this is not difficult to achieve. In higher education, the typing works differently. You don't have a general-purpose professor as a base model. You have a professor in a specific discipline as a base model. if a professor teaches English, and they specialize in teaching about 13th-century peasant poetry, they should be able to also teach other English courses. Moreover, every particular module of a lesson plan should be substitutable for any other in terms of their relationship to the base model of the course, that is to say, that they should all reinforce the same basic ideas that are the bedrock of a given segment of a discipline being taught such that they can stand in for a general theory class on the subject.
Where else can we implement this? In my professional life, I use it most frequently for reporting. When I am making a subtype of a report, a data extract, I only delete the information that isn't being displayed in the final version that is sent to the client. The internal version has hidden or filtered values so that, if needed, I could return it to the original state and subset the data again in a different way. You would be surprised by how much rework this eliminates when client expectations invariably do not match up with the result of what they communicated initially.
More broadly, I think about this principle in terms of avoiding overspecialization. I think a lot about the ways that people who reach peak performance in a given endeavor "break" themselves for other things. In a rather literal example, I think about how professional athletes (bodybuilders, powerlifters, ballet dancers) literally break parts of their bodies permanently, making certain activities impossible for them later and possibly causing themselves a great deal of long-term suffering. We can re-encode this rule for personal use as some variation on "Avoid doing things you can't undo" (said the man with a newborn).
I
The I in SOLID stands for Interface Segregation. The idea here is also difficult to describe to non-programmers. Basically, a user shouldn't have to perform an action that's irrelevant to their use case. So, for a really clunky example, let's say that I have a smart-dryer. The dryer is designed to optimize energy use and so will not run unless there are at least 10 lbs in it and there is a weight sensor internally to determine that. Let's say I got this dryer because it's the best at drying things but, actually, I only need it to for my custom sneaker detailing business, and since I deal exclusively with sneakers, and only work on 1 pair at a time, there will never be any instance where I have 10 or more lbs to put into the dryer. This means that every time I run the dryer, I must put in additional poundage, in the form of things that do not need to be dried but which won't be damaged by being dried and won't damage the sneakers, just to get the sneakers dried. This could have been solved if there were the ability to opt out of the energy optimization mode.
Where have we seen this in real life? Nowhere. We see this nowhere because it's virtually invisible unless we are in the business of designing interfaces for others. It is easier to identify where we see this principle violated than where we see it upheld. A common violation of this principle would be my credit union. Everyone who walks into the credit union has to stop at reception to tell them where they're going (at least the last time I visited, before it was a kiosk). This step is unnecessary for people who have an appointment as they are already expected and will know where they're heading to (the only 2 reasonable purposes for the reception desk). The opposite of that would be a bank where the receptionist waits until you decide that you need to engage with them. The interface is able to be implemented but is not made into an unnecessary requirement, and you are perfectly capable of doing almost anything at the bank without ever accessing that interface.
Another example of this principle being violated almost everywhere is extraneous required fields on forms. Anything for which N/A is a valid response and which requires a response is a field that does not need to be mandatory. On the flip side, there are forms where only the actually required information is marked as required. They are commonplace, and thus forgettable.
The way that I think about how to apply this more generally is to only put necessary points of contact into processes and systems. This is also true with regard to personal systems and processes. So, as an example, I don't have a warmup routine for the gym. It is not that I do not see the utility in a warmup routine. I do. Rather, I do not want to unnecessarily gate the rest of the gym processes by placing that in front of them. If I think some stretching would be nice, I do that optionally. I don't enforce it as a necessary condition of lifting weights.
Please note that this is a horrific bastardization of the ISP but, as I said originally, it is not easy to translate it accurately across domain boundaries.
D
The D in SOLID stands for Dependency Inversion. Dependency inversion can be understood through the lens of policymaking. A policy is sort of a machine to create a specific result with specific inputs. They should be specific enough to be effective but general enough to apply broadly (to avoid susceptibility to rule-beating behavior). In other words, the policies should be based more on general principles than on specific details. So, let's say you are writing a drug policy for a company because someone in the warehouse was caught drinking at work while driving a forklift. A bad policy here would be "warehouse workers aren't allowed to drink at work while operating forklifts". A good policy here would be "it is the responsibility of all employees to be mentally alert and physically able at work. Inebriation or intoxication of any kind will not be permitted". In the case of the first policy, if a general manager working in the offices drank, they wouldn't be violating it because it would be inapplicable. Further, if a warehouse worker came in intoxicated from cannabis, it would also be inapplicable. The exceptions go on, rendering the rule almost entirely ineffective and guaranteeing a need to write more rules.
So, where do we see this in real life? I've already highlighted policymaking. This also shows up prominently in design. Baseball bats, for example, are not designed to be right-handed or left-handed. You could design them that way. You could put finger grooves in to enhance grip and weight it predominantly to 1 side. It makes more sense to design a general bat, one which can be implemented regardless of the hand orientation details, and it will work for all cases equally well without need for modification.
Another, perhaps more broad, example of this principle in action is in inclusive design. Inclusive design principles generally function in a way that makes things easier for people without disabilities and possible for people with disabilities. A ramp in place of stairs does not take away a person's ability to travel between 2 different heights but it makes it possible for someone in a wheelchair, using a walker, or pushing a heavy wheeled cart. If you enslaved the design to the details instead of the principles, there would be a statistical argument that could be made for just keeping it as stairs.
In my personal life, I use this for information delivery design. For example, and with respect to the Interface Segregation Principle, I tend to start my e-mails on complex subjects with a top level summary, or "the short version". The short version invariably deals more with general principles than in the specific details at play. For people who want less detail, they also aren't forced to read through a long e-mail to get the "no" they were looking for the entire time.
More broadly, I would say that Dependency Inversion is a great principle in terms of thinking about planning. Plans created with the details we know and have access to at the beginning must invariably be changed as new details emerge. Plans based on more general principles (read: heuristics and values) will be more flexible and less likely to require change over time.
There will be a part 2 to this article, dealing with iteration, parallelizing, in-memory operations versus retrieval, and other programming-related concepts as they apply to real life scenarios and thinking.
Training | Content Management | Knowledge Management | Talent Management | Customer Service | Operations
1 年Can't wait for Part 2!
I facilitate workshops that support community innovation
1 年I love this Shane. So many folks in STEM education throw around the term "computational thinking" without really articulating what that means and why it's important. Thank you for writing this. I will definitely share it with some folks.