5.1 Tips For Fledgling Software Engineers

Lachlan Ford
7 min readAug 19, 2019

--

Tips for fledgling software engineers you might find useful.

In conversation with an acquaintance that was entering the fantastic field of software engineering, I was asked what some useful tips for a novice software engineer entering the field would be. Since I had not given it much thought in the past I babbled out some kind of answer off the cuff which was barely coherent but was useful enough for my friend to be reasonably satisfied. Since that version of this list had some utility I thought I might take the time to noodle through it further and see if I could produce a more refined version.

Without further delay, here are 5.1 hopefully useful tips for fledgling software engineers getting their beaks sticky and eye bags calloused in the wonderful world of software engineering. Some of these I think are counter-intuitive and not often emphasized, but I have found them quite useful.

When and what to optimize?

Novice engineers will tend to early optimize. Whether this be run-time complexity or space complexity. This can be well and good and produce more efficient code but will often come at the cost of productivity. Like most things in the pragmatic world of professional software engineering this is a balancing act.

A third factor not often considered is life optimization[1]. Sure binary search is the classic O(logn) blue hedgehog of simple search algorithms but its implementation may end up taking more of your finite lifetime than anticipated, not to mention increasing code entropy and complexity[2], whereas the humble linear search is easy, simple and fast to implement. Unless there is a library for the complicated method, stick to the simple and easy method. Especially since that in most cases, the optimization is of a small part of the overall system AND the data you are operating on is likely bounded. Computers are fast these days, so the real time performance difference will likely be negligible.

Most everyday performance gains will be from your choice of data-structures (which will inform your choice of algorithms) in your implementations.

Performance may also not be as predictable as you think. A wiser strategy is to consider your program as a whole and use profiling tools to discover the real performance sinks.

Failing to aim for understanding

Often, novices will dive into a problem without grasping the system they are working in to the extent that they can.

The ability to intuit what to ignore and what information to seek is a skill that is developed from working in a codebase for an extended period of time, and only really developed after most of the system is understood. Often novices will feel that the time investment to understand a system is not worth the upfront cost, which delays the resolution of their immediate problem. This is a trade of long term productivity to short term productivity.

Most everyday software engineering problems will show themselves to be trivial or obvious once their domain is understood. The answers will make themselves apparent.

Productivity may also be killed by changing stuff and seeing what happens (though this can be a technique to gain understanding), or worse, debugging using print statements.

If you have an understanding of the system, problems in execution will stand out to you like a sore thumb. You also cannot ever be confident in an implementation unless you completely understand it.

Understanding also stretches to your tools. An IDE may have many productivity boosting features waiting to be discovered. The best way I found to discover these features was to watch a wide range of developers coding habits. Everyone uses their own personal subset of the feature set.

Of course this is also a balancing act as short term productivity is important. However it is necessary not to over index on the short term and sacrifice the long term.

Invest in them tests

Tests are a great way to lock in your newly found understanding. They also are a great hedge against your own imperfections as a developer. They also take the cognitive burden of much of the understanding of the system from you as you are able to solidify it formally.

As development increases code entropy, over time we will want to refactor every now and then to avoid slowly git pushing ourselves into a spaghetti themed hellscape. It is almost impossible to confidently refactor large codebases without comprehensive test coverage. They are another thing that takes short term time but are one of the most valuable long term investments.

I would also advise to never skimp on the cleanliness and expressiveness of test code. Why? Because you can be damn sure your coworkers will skimp over it in code reviews, so it’s all up to you!

Simplify as much as possible

Like all good little lifeforms, we want to continuously fight entropic decay. The art of simplification is one of the more important skills to cultivate. The number of bugs in a system is positively correlated to the size of a codebase. As Software Engineers one of the worst things you can do to a codebase is to add code to it. Simplification is the art of expressing the codebase as elegantly as possible. Ideally you should be trying to be as lazy as you possibly can.

A simple and elegant codebase will be more understandable and less bug prone. This simplification principle does not just apply to the raw code itself but also to the choice of abstractions, algorithms and modules etc.. Everything should exist for a clear and distinct purpose with logical data flow between systems. There are many books on this so I wont go any deeper here.

Having an intuition for simplification and elegance is also a skill developed over years of experience but can be put into practice fairly simply. Once we have our rough understanding of our system and it is bolstered by comprehensive tests and we may refactor safely to our hearts content, we may begin by simply deleting all unnecessary code, unifying code any duplication and examining how complex or convoluted are our abstractions. Again there are many books on this so I wont go any deeper here.

Often it is good to bias towards correctness first, simplification second. My general approach is to make the implementation as complex and dirty as it needs to be and aggressively trim it down as much as possible when I am comfortable that it is correct.

I’m not entirely sure why simplification is so effective, but if I had to guess it would be because it constrains possible state. The core of understanding codebases is mentally mapping possible states. If these states are constrained there is far less room for bugs and entropy to sneak in. An alternate (or complementary) way to think about simplification may be state space minimization.

Learn and experiment (diplomatically)

Engage in learning and try your ideas out practically. You will find that this will get push-back for many reasons. Firstly, most ideas are bad ideas, and most of the ideas you try will be bad ideas (and that is OK) and the people pushing back probably do know better than you (their perspective is valuable). Secondly, people get set in their ways, as humans tend to do.

The software industry is probably the most dynamic and ever changing industry in existence today. Especially web development[4]; there you type as fast as you can just to stay in the same place. Thus it is important to expose yourself to a wide range of programming techniques and paradigms and stay on top of the latest features.

Introducing these new toys to the real world difficult but is more an effort in diplomacy than anything else. The benefits have to be sold and need to significantly trump the negatives and if possible, should not be significantly disruptive to the existing codebase. Simple right?

For example functional programming is a very useful programming paradigm to follow for a myriad of reasons. It is also a complete mindfuck for most imperative inclined veterans, making it a difficult sell. Applying functional principles to C++ is quite a bit easier and is a much easier and more pragmatic approach[5].

Cultivating a culture of experimentation is a good long term investment for your own growth and for the long term health of the codebase.

People can get highly accustomed to out of date techniques and systems. Your young blood is useful for more than just vampiric nourishment but also for invigorating the codebase.

Perfectionism

Done is better than perfect ;)

References

  1. Jonathan Blow on programming independent games independently
  2. Most binary search implementations are wrong
  3. Bugs to lines of code
  4. “Welcome to web development”
  5. John Carmack on functional programming in c++

--

--