Application Design Decisions in Pretty Diff
Code is Inherently Evil.
More precisely writing code is a form of debt, which is evil. The desirable outcome is not the code itself, but rather the solution to a problem offered by the code. The degree of evilness is a direct measure of the amount of time it sucks from a soul over the lifespan of the application. The ultimate goal is to produce a superior product in the least amount of time with the lowest cost of maintenance. Observe the following criteria:
Hierarchy of Goals to Repel Evil.
Achieve these rules in the order provided to resist the forces of evil.
-
Write stable and defensive code.
Broken buggy code means time spent infuriating your users and fixing defects instead of writing enhancements. Defects increase the evilness of your application, and most defects are not known to you. If you get this correct and fail at everything else, you are still at least half successful.
-
Write fast code.
Faster executing code is less evil code. If your application is slow your users won't have the patience to notice anything else.
-
Write simple code.
Ambitious goals do not mean ambitious code. Always keep your code simple and well organized so long as it does not violate the previous rules. Searching the code base for defects is evil. Deciphering algorithms is evil. Guessing what extends what is evil. Expect other people to read the code and enhancement it without your input.
-
Write code quickly.
Solving bugs in mere minutes impresses people. Easy to maintain code saves you time and it means a faster return on suggestions to your users, which saves them time. Saving your users time is the most powerful weapon in the fight against evil code. Always write the simplest code possible provided it does not violate the previous rules.
-
Write less code.
Anything that can be accomplished in fewer instructions is less evil. Bloat directly increases the evilness of your code. Ultimately, this is the most important rule, but only when it does not impose harm. Cutting corners frequently results in increased evil, so this rule must not be exercised at expense to the prior rules.
Empower Your Users.
If your application saves your users more time compared to its competitors or provides a more enjoyable experience, then it is superior. Game over, you win. To accomplish this the application must achieve these criteria:
-
Solve a valid problem.
Solutions in need of a problem are evil. Simple solutions to complex problems are the programmer's most powerful weapon.
-
Sturdiness and stability.
Does your application break or fail in its tasks? Broken applications are evil. Most defects will not be known to you.
-
Accomplishment of goals.
Challenging goals make for a superior product, but broken promises are evil and embarrassing. Be clear in what you want to achieve and conquer those objectives with iron-fisted certainty. Manage your expectations appropriately. If you are honest and fair to yourself a tiny spark of determination is sufficient to accomplish all.
-
Speed.
Speed in all things. Faster execution speed is what your users will notice first, and so this is supremely important. Provide faster interactions and faster access to the desired results and content. Be active in the maintenance of your software and respond to user communications quickly. Users always take notice of prompt communication and delight in prompt resolution.
-
Understandability.
If your application is not immediately clear, then provide strong documentation and define your purpose clearly. Users will actually read it to learn more, especially if your application is superior to the competition. Encourage feedback and use this to fill gaps in understandability and then improve the documentation when you don't have to. Confusing applications are evil. Confusing documentation is more evil. Communicate clearly, directly, and with a purpose while using as few words as possible. Visual examples are wonderful. Flashy graphics and animations are like a bad blind date in that they attract stupid lonely people, but lose all value after a drunken first impression.
-
Reproducibility.
The more places your application can run the more popular and dependable it will become. Does your application run in exactly the same way everywhere? If not your application is evil.
-
Ownership.
Good software with a healthy following is largely due to ownership. Always own your code. For every success and failure take ownership. For every success be humble and thankful. Address all who helped you on your path to success. If you believe that you have achieved success entirely on your own then you are an evil toxic liar, and many people will believe you.
Own every failure and accept all blame. People want to hear your solutions and assurances of fidelity. Nobody wants to hear your excuses. Uttering excuses is advertising incompetence. Own your code, always.
Be Brilliant
Brilliant people are those wonderful individuals who exercise a tiny bit of creativity to avoid stupidity. This incredible gift makes challenging feats look simple.
-
Never Write for the Lowest Common Denominator
This can best be described as: garbage in, garbage out. Don't plan your application and enforce code styles for the weakest developer. Instead put forth the best possible approach for an organization's business objectives and don't dumb it down. This will require the weak contributors to step up or get out without slowing everybody else down. When this scares people suggest pair programming.
Providing allowances for less qualified developers means making sacrifices. Most commonly this means adding a bunch of, in some cases 10-15 times more, code. It is a run-away snowball (freefall) of increased debt, because more code means more unit tests and liabilities. Remember that nothing is more evil than additional code.
In many other cases it can mean hiding non-existent problems behind unnecessary abstractions to synthetically appear that there is less code when actually there is much more. Due to the nature of lower level optimizations, like instruction caching, in some rare cases abstractions can increase execution performance. Mostly, though, abstractions make code execution noticeably slower. As an example, this is almost universally the case when working with the DOM. That means your abstraction is the point of increased evil and not the DOM.
-
Use Explicit Application Structures
Explicit application structures mean that the code is written in such a way that the structure of the code's organization in written form almost directly reflects its execution order and structure in memory. The goal is to close the gap between the organization how the code is written versus how it is organized in execution. The primary qualities to consider are scope, reference availability, and modularity.
Thinking about code in terms of explicit structures forces developers to think in terms of big problems first and gradually work to the minor trivial items, a top down approach. This is healthy. The big picture items don't have to be specific or precise early on, because they can be tightened up later. This results in code that is well planned, easy to follow, and focused in what it wants to do. The tiny precise qualities are addressed and solved through requirements and unit tests, which require less creativity, so this stuff is lower priority. The result is less code and easier application optimization later.
Sometimes explicit structures can mean deep nesting. This scares the crap out of insecure developers, who may confuse this idea for nested data structures. I don't care. Explicit structures make for faster code debugging and near immediate understanding of relationships in the various code pieces. If as a developer this scares you then I suggest finding a mentor. If as a developer this offends you then you are in the wrong line of work.
-
Never Outsource Dependency Management
If your application fails because it pulls in a broken dependency you are a failure. You can cry, whine, and complain about how it isn't your fault but your application is still broken. If this application generates revenue, then you are now bleeding money. Actually, it is your fault, because you failed to manage your dependencies. Game over.
-
Use Build Systems Wisely
An application build system takes time to run. Think of this a thief robbing you and your business of time or a vampire bleeding you of life force. Essentially it is stealing from you. There should never be a build process separating your code from its execution. I understand most languages require a static compile step, so therefore they are more evil except if their resulting software offers noticeably superior execution performance. The advantage of not having a build system is that you can modify the code and then execute it immediately without any intermediate steps (wasted time).
When there is no build step between your code and its execution the developer can more closely share in the experience a user performs to execute the application. End users are not going to jump through hoops to build your application with a bunch of configuration steps just to run it for the first time, so why would you?
It is helpful to have an automation task to run unit tests, perform code validation, update code and data from a remote source, and so forth. In this case a build system is advantageous when it prevents code regression and automates various required tasks. This saves time or lowers risk. Instead of stealing from you this is more like interest on a financial fund, in that it returns payments of time merely for the pleasure of your initial investment. The faster this sort of automation job executes and the more problems it prevents from entering production the more valuable it becomes.
-
Be Imperative
When writing code seek to accomplish your objective as directly as possible, bottom line up front (BLUF). Don't tip toe around the problem with finesse and elegant descriptions. Well written code is either immediately clear in what it is doing or it is directly inferred from the context and structure of the code's location in the application. When clarity is absent from merely reading the code bad things happen. All the declarative and conventional wisdom are just smokescreens suppressing the symptoms of gross negligence and incompetence that we are all occasionally guilty of. When code is lacking of transparency and clarity then as a developer you are failing: yourself, your teams, and ultimately your users. Go back and rewrite it to be more direct.
One thing declarative coding styles kind of get right is use of naming conventions for references and preference for descriptive and unique reference names. Don't over think this though. Application code is not a novel. Novels are meant to be read by people, but application code is meant to be executed by machines. Reference names have to be unique enough only to avoid confusing a variable in one location for a different variable elsewhere. The only time reference names must be absolutely unique is in the case of identification by automated profiling tools. I have a convention I use to enforce uniqueness on function names that I will never read, because they are for profilers and not for me.
Reference names should be descriptive enough to kind of hint at what they store. This has to be just descriptive enough that the reference name jogs your memory upon reading it so that you don't have to look up its definition multiple times. I have seen people really over think this and waste a bunch of energy on incredibly long reference names. They should save this for their doctoral dissertation that will be read by only 7 people.
You should never need code to explain what it is doing or how it is doing it. You can determine this yourself without the extraneous descriptions by reading the code. Where extra descriptions and documentation come in handy is to explain the why, as in the decisions that resulted in the logic you are reading now. Be helpful and quickly document such decisions in code comments in as few words as possible. Don't document anything else except risks and known problems.
-
Never Use Inheritance
The first reason is that it requires additional code. The extra code exists to satisfy conventions required by the code and not to ease human comprehension. This means more meaningless code to write, read, and maintain. Ask yourself if you would rather read a 2000 page book or a 200 page book containing exactly the same plot and descriptive elaboration.
Inheritance is an unstructured architectural concept. Structures emerge from inheritance through extension of atomic units, which forms an implied or ad hoc application architecture. This is bad. The direct harm is a lot more energy and effort must be taken to form and enforce a well-planned architecture in a given application. The indirect harm is that inheritance tends to result in applications that are composed of many relatively flat atomic units where the structure is only evident at execution time. This is really bad, because it means most effort debugging a problem will be spent searching for locations to troubleshoot in the code instead of thinking about solutions.
If at maintenance time more than 50% of the effort is spent looking for something your application architecture is a failure. If when reading the code, the organization and structure of the application are not immediately clear your application architecture is a failure. If you require a super advanced IDE to find things in the code your application architecture is a failure. Inheritance strongly enables these negative qualities while introducing additional lines of code to slow you down further.
Most of the resistance to moving away from inheritance has to do with a combination of stubbornness, arrogance, and tradition. The first means of combating this ignorance is to admit alternatives exist. If you are unwilling to even do this, then you are evil. Second, determine the benefits inheritance directly provides to your current application and directly challenge those opinions with alternative approaches to see what the result would look like for your application.
Evil Developers
Most evil developers are people who frequently violate the prior guidance. These violations are frequently due to childish immaturity or selfishness. When evident in a leader the common military phrase is toxic leader. Such people are a disease exposing their infectious sickness upon others.
-
Incompetence
When interviewing in the past this is the first thing I would look for. Is the given candidate even remotely qualified? If you focus all your energy on how code should be written you are a beginner. I don't care if you have 15 years of experience with the best names in the industry. You are operating at a lower level of career maturity and I will treat you like a beginner. If you are strong in the ways of identifying and reducing evil, you will already know how to write code well and unintentionally so. Instead of thinking in terms of how think in terms of why and make stronger more defensible decisions.
Unfortunately, incompetence is common. In fact, it is generally the tolerated standard, which is why talent gaps are so prolific in software. This is not because technology is too hard to learn or because people are generally too stupid. Mentoring and training are absent from the work place, which leaves a developer to figure things out on their own. Those rare super talented developers that rise above the rest are people who spend time, measured in years, outside the office solving problems and self-training when they don't have to. Any common developer will see, with clarity, the problems that prevent immediate task completion. You won't see the second and third order consequences of your half-baked resolution without some deeper experiences. If you want to work with a stronger team then introduce mentorship programs, formal training, and pair programming.
-
Women
I am putting a section on women in here just to see if you are paying attention. Women are severely underrepresented in software development and that is a complete failure. Women tend to evaluate balance of requirements, focus on complex problems, and prioritize responsibilities slightly differently than men. That means diversity in common assumptions in how basic problems are approached. I am fortunate, at the time of this writing, to work in an office that is largely gender balanced with most of the women developers substantially more experienced than myself.
-
Whiners
People who complain frequently without effort towards some resolution. This behavior depresses everyone like childhood leukemia. Complaints are only ok when followed by some action towards resolution or when used as a tool for positive interpersonal bonding on a team.
-
Popularists
People who make decisions entirely vested upon perceived popularity lack the research and critical decision making skills to be technologists. Politely introduce these poor confused souls to your marketing team and walk away hoping to never see them again. Trends come and go, but bad decisions remain for a very long time at great expense.
-
Sturbornness
Stubborn is completely different from defiant, but to non-critical people they appear to be the same trend opposing quality. Both stubborn and defiant people can slow a process or rapid decision forming. Stubborn people are evil fools who live and die by a behavior called cognitive conservatism. In a world of really bad technology decisions defiant people are those blessed angles who challenge everything. The defining character difference is that defiant people will surrender when presented evidence or a superior evil reducing argument.
-
Trend Whores
Trend whoring occurs when an individual uses perceived popularity as the only grading metric. Popularity might be a quality worthy of strong consideration, but when you ignore everything else you are evil. This behavior is present when a person completely lacks common decision making considerations or prioritization and evaluation. Responding to popularity is an artificial decision making process because the decisions are already made by numbers presented to you.
-
Analysis Paralysis
When claiming not enough data is present to make a decision you are suffering from analysis paralysis. There could always be more data. Any decision, even a knowingly wrong decision, is better than no decision at all. If you fail to make a decision somebody else will make it for you and impose all its liabilities upon you. Congratulations, you just graduated to scape goat. Stop being a coward, just make a damn decision, and own it like a titan. You can always make a different decision at a later time in the face of new evidence.
-
Cowards
Everybody will at some point experience fear. Cowards are people who deliberately hide from their fears without regard for harm or risk to anybody else. I struggle to find something more evil. This is worse than panic.
In the corporate world there are two common things that people fear: failure and confrontation. Get over yourself. Everybody fails. Learn from it and become more competent. Gracefully recovering from failure or supporting others in the face of failure are strongly admired qualities that are profoundly noticed.
Positive confrontations are good things. This is how business is conducted. So long as you are direct, factual, honest, and empathetic you should have nothing to fear. If you are still afraid you should not be allowed to have a job.