A familiar saying in business is to “work smarter, not harder”. It generally means minimizing tasks and steps, so that one can be more effective or faster with the available resources. This usually means that clever software is written that gets the entire job done. However, requirements change over time and especially with larger projects further along in development, this means that exceptions and workarounds are written to handle the changing workload. In addition, the original team will head off to new challenges and personnel turnover causes multiple handovers, reducing knowledge at each instance.
The gordian knot
If the solution is stable and no new requirements are made, like bronze statues, the solution gains a patina, and knowledge of the features diminishes until a black box with the old pains of the process is left. If development is ongoing, the latest team, through no fault of their own, often lacks the bigger picture and quick fixes are chosen until what once was a beautiful tapestry becomes a Gordian knot.
Eventually, a sword is taken to the knot, and pandora’s little black box is opened for analysis to build the replacement. The old system becomes a legacy system while some smart people start working on the next clever system. As the French say: “L’histoire se répète”.
This cycle is largely unavoidable, new platforms and approaches arise all the time and sometimes a complete replacement is unavoidable. It can however be delayed. Any experienced developer who is also a perfectionist learns that although the code may be concise and elegant, it also needs to be maintainable. Write a bit more code to avoid having to reconceptualize the purpose and ramifications later when modifications are needed. The same principle applies to the application at large. The smart solution is not the best solution if a random next developer cannot oversee the consequences you considered when writing it. I can hear you think “Document!” but documentation takes time, is rarely kept up to date, and is only read in its entirety by the team creating it. When the considered consequences are about the interaction between multiple components of the code, the information could be in various places in the documentation and perhaps not with the component that needs changing. Other issues that affect maintainability are the temptation to take a shortcut, combine different tasks into one component or have different components for the same task. Everyone knows this to be a no-no, but it is a smart fast way to do this change and we don’t have a lot of time, so it does not really count this time and we can do it right later.
the complexity of simplicity
To avoid becoming a black box or a gordian knot, you need a clear and simple design. As anyone who has tried explaining love to a 5-year-old knows, keeping things simple is one of the hardest things to do. Clearly explicitly defined responsibilities and tasks for each step in the process helps. With multiple developers, opinions on where a task should be done can differ, a good design takes that away. A design that endeavors to have one way of working prevents multiple solutions to the same problem. This does not mean that there are no exceptions, but exceptions feedback into the main process as soon as possible, even if they must go back in the process a little.
Between steps, a good design has room for other components to be added. An open structure with room for new additions without having to bypass the design is a design that remains understandable. A good clear design will help future teams to resist the temptation for a shortcut or workaround but encourage them instead to fit their requirements in the existing structure since there is ample room for it.
Therefore, my personal motto is “don’t work harder OR smarter but work simpler.”