Nice article.
https://blog.pragmaticengineer.com/the-product-minded-engineer/
A particular type of complexity is over-engineering, where developers have made the code more generic than it needs to be, or added functionality that isn’t presently needed by the system. Reviewers should be especially vigilant about over-engineering. Encourage developers to solve the problem they know needs to be solved now, not the problem that the developer speculates might need to be solved in the future. The future problem should be solved once it arrives and you can see its actual shape and requirements in the physical universe.
https://google.github.io/eng-practices/review/reviewer/looking-for.html
“prefer duplication over the wrong abstraction”
Programmer A replaces the duplication with the new abstraction.
Ah, the code is perfect. Programmer A trots happily away.
A new requirement appears for which the current abstraction is almost perfect.
What was once a universal abstraction now behaves differently for different cases.
https://www.sandimetz.com/blog/2016/1/20/the-wrong-abstraction
I totally agree. In most cases it’s a good rule of thumb.
– Return as soon as you know your method cannot do any more meaningful work
– Reduce indentation by using if/return instead of a top-level if/else
– Try keep the “meat” of your method at the lowest indentation level.
– Error handling is noise.
http://blog.timoxley.com/post/47041269194/avoid-else-return-early
The development of the user interface for a large commercial software product like Microsoft® Windows 95 involves many people, broad design goals, and an aggressive work schedule. This design briefing describes how the usability engineering principles of iterative design and problem tracking were successfully applied to make the development of the UI more manageable. Specific design problems and their solutions are also discussed.
Keywords
Iterative design, Microsoft Windows, problem tracking, rapid prototyping, usability engineering, usability testing.
However, the fixes would not have been made if the team had not believed in making the most-usable product possible. Key to this belief was our understanding that we probably weren’t going to get it right the first time and that not getting it right was as useful and interesting to creating a product as getting it right was.
https://socket3.wordpress.com/2018/02/03/designing-windows-95s-user-interface/
Highlights from https://www.simplethread.com/software-complexity-killing-us/.
We get enthralled by the maelstrom of complexity and the mental puzzle of engineering elegant solutions: Another layer of abstraction! DRY it up! Separate the concerns! Composition over inheritance! This too is understandable, but in the process, we often lose sight of the business problems being solved and forget that managing complexity is the second most important responsibility of software developers.
Looking forward to the future, it is unlikely that language design will give us the same kinds of improvements we have seen over the last few decades.
We, as an industry, need to find ways to simplify the process of building software, without ignoring the legitimate complexities of businesses. We need to admit that not every application out there needs the same level of interface sophistication and operational scalability as Gmail.
Our answer to the growing complexity of doing business cannot be adding complexity to the development process – no matter how elegant it may seem.
We must find ways to manage complexity by simplifying the development process. Because even though managing complexity is our second most important responsibility, we must always remember the most important responsibility of software developers: delivering value through working software.
Shows how to do it.
#
Keep it simple.
Collaboration: Helping others is a priority, even when it is not related to the goals that you are trying to achieve. You are expected to ask others for help and advice. Anyone can chime in on any subject, including people who don’t work at GitLab. The person who has to do the work decides how to do it but you should always take the suggestions seriously and try to respond and explain.
Efficiency: We care about working on the right things, not doing more than needed, and not duplicating work. This enables us to achieve more progress which makes our work more fulfilling.
Iteration: We do the smallest thing possible and get it out as quickly as possible. If you make suggestions that can be excluded from the first iteration turn them into a separate issue that you link. Don’t write a large plan, only write the first step. Trust that you’ll know better how to proceed after something is released. You’re doing it right if you’re slightly embarrassed by the minimal feature set shipped in the first iteration. This value is the one people underestimate when they join GitLab, the impact both on your work process and on how much you achieve is greater than anticipated. In the beginning it hurts to make decisions fast and to see that things are changed with less consultation. But frequently the simplest version turns out to be the best one.
Surprisingly the ability to use basic imperative programming constructs very efficiently in order to implement something is, in my experience, not as widespread as one may think.
An experienced programmer eventually knows how to deal with a variety of sub tasks. This avoids both a lot of design work, but especially, is an extremely powerful weapon against design errors, that are in turn among the biggest enemies of simplicity.
Internal factors are procrastination, lack of interest in the project at hand (you can’t be good doing things you do not love), lack of exercise / well-being, poor or little sleeping.
External factors are frequent meetings, work environments without actual offices, coworkers interrupting often and so forth.
Often complexity is generated when there is no willingness to recognized that a non fundamental goal of a project is accounting for a very large amount of design complexity… A project that is executed in order to maximize the output, is going to focus exactly on the aspects that matter and that can be implemented in a reasonable amount of time.
the two main drivers of complexity are the unwillingness to perform design sacrifices, and the accumulation of errors in the design activity.
An initial design error, in the wrong hands, will not generate a re-design of the same system, but will lead to the design of another complex solution in order to cope with the initial error. The project, thus, becomes more complex and less efficient at every wrong step.
each time a complex solution is needed, it’s important to reason for a long time about how the complexity can be avoided, and only continue in that direction if no better possibility is found even considering completely different alternatives.
things like robustness, simplicity, ability to deliver in time, are often never accounted for.
To be a super expert of everything is not required, but to be at least aware of a multitude of potential solutions for a problem certainly is.
Good competence of C, the understanding of how CPUs work and clear ideas about how the kernel operates and how system calls are implemented, can save from bad late-stage surprises.
The sum of being good at gaining state about a bug, incrementally, in order to fix it with a rational set of steps, and the attitude of writing simple code that is unlikely to contain too many bugs, can have a great effect on the programmer efficiency.