Engineering Principles
These are my go to principles when it comes to engineering. A reminder to myself when picking a path for a project.
- Understand the problem
- Focus on the data
- Choose reliable technology
- Use less dependencies and abstractions
- Work with the platform
1. Understand the problem
If you don't understand the problem correctly your solution will either not fix the original problem, or be over-engineered, creating more problems over time.
For example, if you need to transport a piece of furniture to the next street along, your first thought might be you need to buy a van. Or maybe for this situation a simple furniture trolley would fit the problem better. Definitely less cost and maintenance.
However if its not the next street along and instead the next town, then you probably do need a van.
But how often do you need to transport the furniture? If its only once you could just rent a van, if its more often then it would be wiser to buy one, and take on the long term cost of ownership.
Keep in mind, as the problem changes, so does the solution.
Continuously evaluate if your solution effectively addresses the problem. Trying to be too generic with a solution (through abstraction) ends up often not solving the original problem, and instead moves it along to whoever is now using the abstraction.
It's ok to throw away a solution that no longer fits an evolving problem.
Utility functions for example, if the problem is outputting a specific heading format, you can use a utility function for the format you require. However if you take that too far and instead create a "this can be any possible format" function, the configuration the function now requires to tell it how to format is almost the same, or maybe even more complex than just using or writing the underlying utility to format directly.
Using a function in its simplest, most direct form provides greater clarity about its purpose. By adding layers of abstraction, you're often not really solving the initial problem, you've abstracted it and pushed the problem along to someone else to figure out.
Is the solution complex because the problem is naturally complex? Or have you made it more complex by adding "what if" abstractions?
"The computing scientist's main challenge is not to get confused by the complexities of his own making." - Edsger Dijkstra
2. Focus on the data
To paraphrase Mike Acton "The purpose of all programs is to transform data from one form to another". Whether thats transforming coordinates and mesh data to pixels on a screen, controller or mouse clicks to actions in an app, analytical data to charts and graphs, descriptions of tests to passes or fails, streams of data into pulses over a wire, or even calculating the average across a list of numbers. Its important to not lose sight of this. Everything is about transforming data.
Understanding the data is critical, what format or structure is the data on both sides of the transformation? How much data is there? How often does it need to be transformed? Knowing this makes writing programs far easier, and much clearer.
Data and data structures are number one, code and how to transform the data is secondary.
"Show me your flowchart and conceal your tables, and I shall continue to be mystified. Show me your tables, and I won't usually need your flowchart; it'll be obvious." - Fred Brooks
3. Choose reliable technology
The more time spent fighting unreliable software, the less time spent on doing the thing you originally wanted.
Experiment with new technologies in small places, a new technology definitely has its place, it solves a specific problem, but how long will it continue to solve that problem? How long will it get security patches? Long enough to outlast your service/app? It's hard to know without time and evidence.
If you do choose a new technology, have such an understanding of the technology that you can take on the cost of ownership and grow it yourself if needed.
A lot of the time you're betting on the technology foundations of your app/business. Be careful.
Ultimately, weigh the technology against the problem and resources you have, a general rule of thumb is "boring" reliable technologies that have stood the test of time tend to be a safer bet.
4. Use less dependencies and abstractions
Always be asking, can you remove this dependency? Or does this abstraction still make sense? You want as much control as possible to shape your solution to fit the problem you're solving.
When you add a dependency you lose some of that control. That might be ok because there's a bigger problem that dependency is solving for you, for example a CSS library providing visual consistency across different web stacks. Or it might be a platform dependency, like an operating system API letting you spawn window objects. Either way continue to check if its still required.
Understand you're at the mercy of the library/service/technology you depend on.
For some abstractions it could be that they no longer makes sense and actually hurt the code base. Especially when abstractions have been made early on with assumptions rather than realistic examples and ranges in mind.
Just as in construction, the integrity of a bridge depends on the materials it's made from. Using too many different kinds of materials can complicate the joins and make the bridge more difficult to maintain.
5. Work with the platform
Have a deep understanding of the underlying platform you're building for. If this is the web, then understand what built in features browsers have, what JavaScript engines they use, how their render pipelines work, understand web protocols (HTTP, Web Sockets), understand the range and typical network conditions, understand the server architecture and range of hardware you're building for.
If this is a native app, understand what APIs and system calls the operating system makes available, the range and typical hardware architecture of the devices running your app.
By working with the platform you can engineer your solution to fit well with the reality of the platform/hardware you're building on, requiring less code, less abstraction and ultimately less maintenance.
One more thing to keep in mind:
We work with machines, its our job to make those machines do useful things.