Recently I held a presentation at my day job to catalyze some discussion around careers, software engineering and spark developers in our company to think about the soft side of development. To do that I wanted take some time and reflect on my time as a software engineer. This post is an essay about stages on a software engineers career and dimensions of engineering competency that affects our aspirations to become a better developer based on the presentation I held earlier.
We’ll start with engineers’s career stages which I have divided into 4 different levels that I consider people go through before either feeling comfortable enough in the chosen location or moving into different direction in their career or being forced into a more ‘non-technical’ role. May the fourth being so strongly in my mind at this time, I have named these levels after the stages on Jedi training.
I think of a youngling as someone who is coming straight out of education pipeline and working in their first job(s). They usually start eagerly and tackle tasks given to them with great energy. Depending on enthusiasm on the occupation a youngling moves either very fast or very very fast, but regardless is moving forward at a fast pace. They have so much new information to sponge from the environment that at times it might seem too much or overwhelming.
Their discussions at work usually revolve around tasks, patterns used within the code base and syntactic issues of chosen language, framework or library. Difficult aspects of programming at this point are design patterns, common best practices and in general trying to find a comfortable way to deliver needed tasks.
The way out of this stage is to crank out as much code as possible, creating side projects, investigating technical literature and patterns of chosen language. Trying out those patterns, and usually ending up overusing them for the sake of training.
Terminology: Tasks, Syntax, Codebase
After much of the syntactic issues have been overcome the next step is usually a stage of arrogance. A padawan is capable of discussing code, understanding syntax and isolated strategies of abstraction, they also know the rules of a code base, language and framework fairly well. The amount of WTFs/minute as a code quality measure is usually the highest at this stage, regardless of the actual quality of the code. Because the rules have been learned it is easy to spot mistakes in other peoples’ code. It is often “unfathomable” how bad code other people tend to write.
A padawan discusses issues with the team, fleshing out pieces of features whether or not actually understanding the underlying business value they provide. They are able to suggest possible solutions and offer a helping hand in desigining individual execution strategies when doing tasks. At times a padawan gives some helpful tips to a youngling and helps them reach the next level.
A part of the trial-by-fire in this stage is a time slot dedicated to over engineer a feature, helpful library or similar. This is a good learning step on the path to climbing out of this stage. An over-engineered solution might receive praises like “so genius that I can’t really understand the code”, which is a good indication of raw talent that a padawan might possess. Eventually understanding trade-offs of over engineering and how the sand below every business decision, and therefore the code base as well, shifts inevitably gives padawan a good understanding how simplicity and modifiability trumps ‘a perfectly executed abstraction’ more often than not.
Terminology: Frameworks, Patterns & Abstraction, Features, Developers
As seniority grows a bunch of maturity enters into the equation. Arrogance fades away when understanding on trade-offs becomes more apparent and the vision expands to see the multiple signs of a single coin. At this stage an engineer will take their initial steps to start architecting subsystems. They are capable of jumping into business discussions, somewhat guiding product owners, business analysts and clients towards correct engineering decisions. They also usually have enough knowledge about the duality of business domain and the technical system. That gives a Jedi Knight the ability to guide their team to make correct decisions when thinking about building, enhancing or modifying a new requirement into the system.
Due to longevity of a career and experience on multiple paradigms, languages and frameworks they are able to bring fresh ideas from other scenes and apply those into the current solution. A big shift on an engineers career usually comes at this stage. The amount of screen time one has available becomes much less and focus shifts towards driving business decisions, overseeing feature building and guiding others to do a good job. Because of that the mentoring aspect of a job also increases quite a bit at this stage. Jedi Knights have their own Padawans, usually because they are a team lead or a principal developer. They spend a lot of their time mentoring other developers within the team and at times solving interpersonal issues that might arise.
“A junior person’s job is to find answers to questions; a senior person’s job is to find the right questions to ask.”
Note that for many people on our industry this might be their last stage. At times career aspirations change and instead of focusing on a technical career maybe a more business facing role is more suitable. Or maybe just focusing on a hard technical task is satisfying enough and there is no need to climb levels any more.
Terminology: Paradigms, Business value, Architecture, Teams
The last step on this made up hierarchy is the level of being a Jedi Master. They usually take the role of guiding teams and composing systems. One of the most important qualities an engineer reaching this stage has is foreseeing consequences of decisions made. They are able to see possible downfalls in taken direction and therefore also capable of diverting it towards a better path.
Usually daily tasks at this stage involve deciding product road maps and long term technical directions. A Jedi Master is capable of modifying processes and molding working culture to enhance decision making and directions within teams. They are more and more moving into people management category and most hard problems are working with personalities. Hard engineering problems at this stage involve decisions concerning the whole system of the application they are developing. This includes all the technical stuff like decisions around languages, environments and release processes as well as more business-y decisions like product road maps and resourcing.
At this stage an engineer has gained an understanding that the rules learned in previous stages are meant to be broken. They are able to bend them to find solutions to complex problems. Understanding that simplicity, proving an actual product and creating ‘business value’ are in the end the most important aspects of our industry.
As stated previously, not that many people achieve or want to achieve this stage on their careers.
Terminology: Systems, Products, Companies
“I think it’s beautiful that most of the time developers both start out as and end up as scrappy hackers.” -Andreas Blixt
If we think about the skills a software developer will find useful on their journey we can boil those down into three different dimensions. I have chosen to make the expected split between technical skills and soft skills, and then accompany that with maturity. here are the definitions of these dimensions:
- Tech - Matter
- Skills in: Programming languages, tools, frameworks, libraries and algorithms etc.
- Everything a university usually teaches to you.
- Team - Space
- Skills in: Negotiating, managing people, empathy and being a good co-worker.
- Communication and leadership skills
- Maturity - Time
- Mistakes made, blood drawn, blood bled, decision making skills and self-confidence.
- Mental side of development
Note that not being the best in all of these is needed to be a good programmer. Some decide to take a path to focus on more technical aspects and are happy on that path. They might value hard problem solving and algorithms, having a never ending drive to learn more about tech. Some might not be so interested in hard tech and want to focus on team values more. Their career paths might divert into [agile buzzword] people, dev management or business analyst roles.
The only thing that in my mind seems to keep on growing is maturity. That will naturally enhance both technical skills as well as team work.
In the following subsections we’ll go through few observations that might be helpful for an individual to become better in each of these dimensions.
Tech - Matter
Becoming technically better has a lot material written about it. We have the standard books around software engineering industry, our technical literature and an unfollowable amount of posts on the internet that tell us tips and tricks how to do things. A whole industry has been developed to advance people’s technical skills, and for a good reason.
Below I have listed few things that I believe have helped me throughout my career to become a better programmer technically.
ABC - Always be coding
Aim for 60 hours per week
Uncle Bob mentions in Clean Coder that everyone should be aiming to do 60 hours of coding per week. If you want to become technically very good that is probably a good advice. We should split that 60 hours into 40 hours for work and 20 hours for ourselves. Work gives you structure, real business problems, and lots of edge cases to solve. Especially the ability to tackle real business problems is useful. If we think about edge cases we know that tackling those will give us appreciation of clean and simple design, something that is adaptable whenever the pointy haired boss decides to change the direction.
The other 20 hours working for our own amusement should go towards side projects, trying out new tools, frameworks and languages as well as reading articles and tech literature. Granted, 60 hours per week takes a lot of effort and it might be difficult to squeeze that in into your life. Luckily we can usually be certain that in civilized world we should have a good work-life balance and 24 hour on call positions are isolated to workaholic Americas and 80 hour weeks to the Valley. That’ll give us a clear separation between advancing ourselves at work and advancing ourselves for personal gain.
Luckily we can take somewhat of a breather knowing that most of the extremely tricky parts in tech are learned early on in a career. The real hockey stick curve of complexity is faced when we try to take our first steps in programming, trying to write those first if-else clauses. After that a lot of the things we face are more or less variations of the same things we have seen and learned before. It is also important to keep in mind that eventually there is somewhat of a glass ceiling when it comes to learning tech. There is simply too much to keep up with and sometimes viewpoints, career directions and our interests change when we grow older, making the learning process less rigorous or becoming more focused on other things. Including but not limited to systems thinking, being a better team player or a family person.
1.01 > 0.99
There is an another approach that I like to follow, especially on hectic months and years when 60 hours a week just seems way too much. This comes from a motivational poster.
- 1.01^365 = 37.8
- 0.99^365 = 0.03
Things we are learning tend to accumulate on top of each other. Therefore the calculation mentioned above makes a lot of sense. At times there might be shortcuts between paths but gaining deep knowledge does need a foundation that it can use to build on top of.
In the end, a small effort everyday accumulates a lot over time. It doesn’t take much daily to become better. I believe that this daily practice is one of the reasons why we have these mythical 10x developers. When comparing someone who practices daily to someone who half-asses their professionalism the gap grows fairly large very fast.
Know your tools, no matter what they are
Physical tools and OS
As a software developer our life revolves around our computer. I, of course, would advocate everyone to use their favourite Linux distribution and customize it to match their need. If you stuck are using (nowadays arguably sub par) Mac that is an unfortunate lock-in. Regardless of the system it is important to learn to customize our tools. We need to learn our operating system, how to use the command line to our advantage and how to customize the system to be the best for the task in hand.
It is also smart to get comfortable with our physical tools. Finding the correct screen sizes that work for us. Sometimes the best way to do deep work is to have the least distraction as possible and work on a single screen, sometimes you need a browser, devtools, a terminal and a debugger visible at all times. Another things that we should be paying attention to are our keyboards, mice, desks and working positions. In the end we have to face the fact that we are stationary most of our working days, hunched over that keyboard. Pimping out these physical tools is a tiny price to pay when you compare it to the never ending back pain, carpal tunnel or bad posture and 10 kilos of fat that you will accumulate without them.
We need to know these things by heart and we should be able to use them from muscle memory. That way we can focus on the code itself and not spend time on insignificant issues.
IDE and debugger
As we all know at this stage, both Vim and Emacs are obsolete, the argument has been dead for years. That being said, we should be aiming to use the best tools possible on the software side as well. Whatever our chosen IDE is, same words go for it as they go for our OS. We need to learn keybindings, features available, shortcuts to spin up, deploy and debug our application when we are running it. From the time we spend staring the screen a big part goes to staring at the IDE. To reduce distractions to the thought process we should know every aspect of it. Also if our chosen tool doesn’t fit our need, we need to be aware that there are other options as well.
Make yourself lazy to extra effort
Being a lazy software developer is a good thing. When saying this I am not talking about procrastinating on HackerNews, I am talking about getting frustrated to updating that Jira ticket, clicking that CI build button or writing that same fecking description to a merge request for the millionth time. We should aim to automate tasks done regularly, saving the time taken to do them as well as saving the context switching that is disturbing our workflow.
Learn to read code
We should aim to always spend some time to read libraries, frameworks and code written by someone else, with a completely different signature than ours.
The most important and overlooked thing is the ability to read code. Writing is easy, it is trivial to make things work with code. You can hack and slash, try things until they fit into place to achieve something that works. We rarely consider that most of our time in front of a screen is spent on reading code. Therefore it is very important to understand how the data flows through the code base, how an application is wired together. Whether it is back-and-forth flow from an API to DB and back or event sourcing driven architecture.
We’ll touch this a little bit more later but understanding the whole code base is a difficult problem. If one is able to see subsystems and patterns throughout the application it will make them a better developer without a doubt. This of course depends a lot on how the code base is written. I’ve personally found that in the current climate I am not that much of a fan of Java-style object oriented programming any more. Side effects hidden within objects makes it very very difficult to follow the data flow. Instead I prefer the movement towards declarative style, keeping data structures shallow and dumb. This seems to introduce less cognitive load and makes it easier to reason about the code, making it clearer to follow an execution path from end to end.
Try out other languages and paradigms
“To learn a language is to have one more window from which to look at the world”
On hard tech the biggest leaps I’ve made have come from taking on something completely different than my technical comfort zone and tackling some decent sized project (let’s say few months of 15–20 hour weeks) with it. Things like building toy apps with Haskell, Elm, Clojure, automating deployment processes with Ansible, adding a monitoring solution with Prometheus and playing around with time-series databases or creating an isomorphic app with React/Express were these semi-sized things that took me away from pure back end Java development world. Exposure to different environments helps a lot since it gives so many new ways of thinking about the “main” environment. With this exposure we gain the knowledge of seeing more trade-offs on both sides of the fence and it gives us more ammunition to tackle a problem, since there are multiple approaches available.
- Pragmatic Programmer - Andrew Hunt/Dave Thomas
- Clean Code - Uncle Bob Martin
- Code Complete - Steve McConnell
## Team - Space
Nowadays software development is very much a team game. Long gone are the days (if they ever existed) when a single guy from their moms basement was able to ship something meaningful. When working amongst other people we as developers need to of course be able to work with them, not work despite of them.
The literature on the technical soft skills is boiled down to a few well known books and resources. On top of that we of course have the rest of the world teaching us how to be a better person and a good coworker. I believe these aspects of development will give us a better skill set when the sole goal for us is to climb the corporate ladder. In the end, as mentioned previously, there is somewhat of a glass ceiling when it comes to career development on a pure technical side of our industry.
“Practical skills are easily acquired. Personal skills and greater self-awareness are what really fast-track you.” -L33tbro on HackerNews
Soft skills, as anything else are things that can be practiced. Working in a team, attending events, having pints, playing team sports, travelling, meeting other people all give good material to train and practice these skills. Below are few tips that I’ve found useful in a company environment when interacting with other people.
Practice Egoless Programming
All code is bad from someone’s point of view
We as programmers have a tendency to see everything as bad, legacy, fugly hack or something similar. That comes from not understanding the journey a single line of code has taken to reach that point in the codebase.
For these occasions we have to remember that paths we as developers have taken are different as well. Someone might come from a corporate place where office politics have played a large role, where architectural and stylistic decisions are dictated from an ivory tower. Someone might come from a startup where shipping fast and breaking things has been the norm. Regardless of the background, we have to believe that everyone is doing their best and tries not to be malicious when they are working on a code base. It is important to understand and accept that you will make mistakes, everyone will make mistakes. If such mistake has appeared from somewhere, usually the best approach is not to be a dick about it. It’s also good to remember that there might be good opportunities to mentor other people on things they might be not aware of. In the end mentoring is probably the best way to learn as well. Mentoring gives you an incentive to appear knowledgeable about the issues you are teaching, forcing you to learn more on the matter as a side effect.
Hol’ up, sit down, be humble
No matter how good we think we are, we must remember that there is always someone who is better than us. Instead of feeling devalued or insignificant, we need to flip the mindset on that and see a brilliant learning opportunity in the situation. If someone is reviewing our code we need to understand we are not our code. We need to learn to take criticism, admit ignorance and be flexible. Even if we have spent the better part of the last week working on a bit of functionality that ends up being the wrong one.
That is something very important to be able to do when working in a team and communicating with colleagues. Inflexibility or not being able to take criticism doesn’t help us nor our team. It locks us down into a meaningless arguments and cycles, leading to either broken communications or bad working culture. In the grand scheme of things I genuinely believe that it is more beneficial in the long to hire a person with less experience, someone who is capable admitting they are wrong or that they don’t know things, than someone who is a self-proclaimed 10x dev, someone who believes that they are always correct and are unable to change their point of view to take others into account.
Make friends with QA
I have been very fortunate on my last few employments to work with very smart QA people. What I’ve learned from those stints are that we should really make QA our best friends in the company. The reason behind that is twofold. Firstly we all end up making bugs at some point in time. If QA doesn’t like us, it makes it so much more painful to actually receive that Slack message or Jira ticket that tells you how bad of a job you have done.
Secondly I find it a good learning opportunity to talk with QA. They are usually closer to the mindset of the user of the application than we are. They give us an easy bridge, being the people between completely non-technical customers and absolutely too technical code monkeys.
In the end QA is there to teach us what we did wrong. Knowing what we did wrong gives us a better opportunity to not do it in the future, therefore making us by definition a better developer.
Enforce Good Communication
Listen more than you talk
Decision making is one of the most important individual things we as developers do daily. We want to make our decision from the best possible place. By listening more we gather more knowledge and are therefore in a better place to discuss ideas and make decisions. Generally our industry contains a lot of introverts which gives louder mouths more time and space to speak their minds. Since we are working in a team environment that is definitely not a good thing. The more ideas we get thrown out there the better our decision making ability is.
Mind your language
Especially when talking online. I usually think of my tone before I put in question words. It could be just me but starting sentences with ‘why’, for example, might come out as aggressive or accusatory when seeing those directed towards ourselves online. The difference between ‘why did you do this?’ and ‘how did you reach this solution?’ online is somewhat similar to ‘why the fuck did you do this?’ and ‘can you explain me the thought processes behind this decision?’ offline.
When we are talking about code, we should be talking about code. At that point it doesn’t have an owner, it is owned by the whole company or the whole team. There are few viewpoints we can think about that from. One is that, because everyone owns the code, we need to feel embarrassed about the code we have found in the code base, making it more beneficial for everyone to make it better, leaving the camp site cleaner than when we found it. The other one is that we don’t have to feel embarrassed about the code we have written, not needing to have to feel that bitter feeling of letting go when someone touches ‘our code’. Because of that it’s extremely silly to leave those IDE generated comments on the top of file telling who wrote this and this class. All code is owned by the team. If we need to find out who to discuss about the thought processes they’ve gone through to an individual line of code, we have version control telling us that information already way more reliably.
Disagree and Commit
It is not always possible to come to an agreement. Amazon famously brought their 14 principles of leadership where disagree and commit was one of the steps. There is no reason to stay fretting about a disagreement over decisions made. It is better to bring in disagreements in the early stages of decision making process, after the decision is done, there is very little useful that can be done about it, apart from committing to fully implement whatever needs to be done to reach the decided solution.
Don’t dismiss ideas, don’t blame others, offer alternatives instead
A snippet from Basecamp’s company blog:
“There are two things in this world that take no skill: 1. Spending other people’s money and 2. Dismissing an idea.”
“Dismissing an idea is so easy because it doesn’t involve any work. You can scoff at it. You can ignore it. You can puff some smoke at it. That’s easy. The hard thing to do is protect it, think about it, let it marinate, explore it, riff on it, and try it. The right idea could start out life as the wrong idea.”
Sunk cost fallacy, being emotionally invested into an idea because you have spend so much time generating it also has an opposite. Being emotionally indifferent about an idea because you have invested no time on it. Being empathetic and understanding that someone has tried their best to formulate an idea is important to grok. We are unable to completely understand other people’s though processes but we can, and should, try our best to see their point of view. It doesn’t necessarily mean that we have to agree with the direction taken nor completely embrace the idea. It means that we do need to understand why they have made the decisions. We need to be able to put ourselves in their shoes and see where they are coming from, both from emotional and rational perspectives.
“Most software is intended for a human audience. And so hackers must have empathy to do really great work. It is probably the single most important difference between a good hacker and a great one. Without empathy it’s hard for people to design great software, because they can’t see things from the user’s point of view.” - Paul Graham
Venture other languages and cultures
Like learning technical skills, I think also soft skills benefit a lot from learning another language, travelling to other cultures. Much like learning new paradigms when programming, also learning new cultures shift our mindset towards more empathy and give us more understanding and ways to possibly understand how other people think.
Isolationism and living in your own bubble are never good things, whether it is ultra-tech circles in the Valley or borderline racist xenophobe circles in Lincolnshire, UK.
- How To Win Friends And Influence People - Dale Carnegie
- Clean Coder - Uncle Bob Martin
- Soft Skills: The Software Developer’s Life Manual - John Sonmez
“Bad engineering cultures are measured in WTFs-per-minute. Good engineering cultures are measured in ROFL-copters-per-minute. When talking about the exact same things.”
- Reginald Brathwaite
Maturity - Time
The third dimension that I’ve chosen being part of software developers competence is maturity. This is something that is more difficult to teach, it takes a lot of hands on experience and time to gather needed levels of knowledge in this area. I have read from somewhere a beautiful quote referring this. “Senior developers have bled more than juniors have even tried.” That bleeding will have its effects on us and that will grow maturity on devs. That is also the reason a wording like ‘bleeding edge’ exists. With maturity, we also gain more boring approach to situations.
Still, there are guidelines that can be listed. We can gather best thought processes and decision making patterns that might help us making better decisions. To me, learning more about decision making is one of the most interesting aspects in our industry. There is a fine balance between trying to find a way to be wrong just enough times to learn, but not too much to come out as a fool.
“Only those that have travelled the road know where the holes are deep” - Chinese Proverb
Don’t chase the shiny
Software industry moves fast and because of that it gives us the ability to just ‘enjoy the ride’, following celebrities or ‘rockstars’ and driving the latest trends. At times this might work but anything in moderation is probably the best approach to this as well. Gaining true knowledge is difficult and there are many paths you can take to attain it. When considering the new framework, new library or new language we have to think about the motivations of the publisher as well as the reasons why we are interested in it.
Facebook created React, which I love, for internal use, then started using it as marketing tool to hire devs. Kotlin, which I love, is created solely to sell more Jetbrains IDEs, they have been publicly saying that from the beginning. Someone making their own library might very well be on a personal branding journey where they are collecting Github stars.
It is important to be smart when making decisions about what shiny is good, what is shiny only because it is made to reflect something bright. This is difficult and we can’t always be right about this. At times we miss out and need to catch up, at times we jump into the wrong hype train and need to track back.
We as programmers very often tend to come to a conclusion that everything done before our time is, quote, ‘shit’, unquote. We criticize solutions made before for the sake of criticizing. Working on a legacy code base is deemed unpleasant and every time we can, we want to somehow carve our own piece of empty land, a greenfield project out of it. It doesn’t matter whether or not the current architecture supports our aspirations to create something ‘that is our own’.
That is understandable, legacy code is hard, a lot more thought process has gone into thinking and writing that individual piece of code than we can understand. Humans are usually not able to comprehend all the decisions made for a piece of code and therefore rather than try to understand it, how the data flows, how objects work and why it was written as it is, we usually deem it unusable and opt for a rewrite. I don’t think rewrites work, I have never seen a rewrite succeed. I challenge you to show me one project that has been rewritten, not refactored, rewritten, and it has been a success.
In the end we have to ask ourselves; Which one do you want: 10 years of experience or 10 times 1 year experience? We can’t always be starting from beginning on everything you do.
Trade-offs, there are always trade-offs
What I’ve figured out to ask myself always is that what are the trade-offs on every decision I am making. There will always be trade-offs, so in the end the actual important decision that we are making is to pick the smaller subset of those bad aspects. These trade offs come in many forms, whether it’s ease of hiring and ease of training, making the function “correct” and actual technical superiority, or shipping faster, or following the industry hive mind. It is never a black and white decision. We need to understand that Computer Science and Software Engineering are two different fields. Computer Science wants to find the perfect solution to a problem, Software Engineering the best working solution to a problem.
“Something being objectively “better” doesn’t mean it’s a good engineering decision to adopt it.” -GNULinuxProgrammer on Reddit
Use your subconscious to your advantage
It is well documented in the programming world that banging our head against the wall is probably not the best approach to problem solving. Trying to force our way through the problem by staring at the screen rarely works. If we step out of the environment, make ourselves distance from the task at hand and let our subconscious do its work we might end up with a better solution. The best possible idea might come to us when we are at the gym, taking a shower, or sleeping.
For me the decisions come at night, three hours into sleep. I’ve noticed this to be more of a pattern than I would like it to be. For those of us that stumble upon the same pattern, keeping a notebook on a bedside table and writing down those solutions will give you the ability to fall asleep faster.
I feel that as knowledge workers we should be above the old asses-in-seats management style. Programming can be forced only onto a certain limit. Making decision and finding correct, sometimes even artistic, solutions to problems does not come by forcing them. The same can be extrapolated into team levels as well.
“Everyone knows it takes a woman nine months to have a baby. But you … think if you get nine women pregnant, you can have a baby in a month.” -Theodore von Kármán
Believe in yourself
Know that impostor syndrome exists
Of course at times we feel inferior on what we do. It could be that someone has luckboxed on to learning the exact solution you have been struggling with for months and that makes you feel down, or someone has shipped a nice shiny project on Github with 100% test coverage and a beautifully crafted codebase. We need to remember that if someone posts a code snippet into Twitter, that has probably been brewing on the background for months. Same as a project released on Github might be fresh repository cloned from a years old project.
Software engineers have always been the early adopters of social media even if we didn’t know it. Like watching pictures on Facebook or Instagram, seeing the socially polished version of someones life, when we are reading at someone else’s code, we are reading the polished version of it. What we are usually missing are the actual steps anyone has taken to reach their final solution. There is always multiple failed attempts behind every solution. I don’t write perfect code, far from it. I copy-paste crap from Stackoverflow without second thought, I write “fugly hacks” everywhere, sometimes because I can’t think of a better solution and I’m too tired with “this shit”, sometimes because I don’t actually have the needed knowledge to do better.
Be courageous when diving into the code base
Note that courageousness does not triple-equal fearlessness. Fear is a good thing, it gives us that nagging voice to remember to actually compile, run and test our program before shipping it. Everyone should be afraid of a large code base, but everyone should strong enough to work on it regardless of that fear. This actually applies to both cases, hard and soft; whether it is a technical decision made about hard things or a business decision made about soft things.
Like mentioned earlier it is very important to commit, whether or not we are certain that the solution is 100% correct. We might be disagreeing with ourselves about the correct approach at times. Usually trying out a solution and seeing whether or not it will work is a better step to take than marinating in indecision.
When working with software courageousness comes a lot down to reading the code, reading the systems and understanding the overall structure. That is why it is so important to understand what the patterns, architectural decisions and structures exist in the code base. When the data flow is understood it becomes much easier to be fearless when modifying code. We know what we will do has an effect on a small subset of things. We are aware that changes we make might not be perfect but at least we know where to fix them if we ever find a better approach.
The thought patterns on courageousness usually change the further we go in our career. When we get older, more removed from the code base there is often somewhat of an aversion towards new technologies and approaches. That might be because of fear of change, or might be because we have seen a similar solution fail earlier already. Whatever it is, it is important to remember to move forward and not stand still only because of fear of the unknown. We have to understand that deciding to do nothing is a decision in itself as well, and if that decision is done because of fear, it will probably end up being the wrong one.
Make mistakes, and then fix them
We shouldn’t be afraid of making mistakes either. Mistakes are the things that teach us the most. It is important to make mistakes, whether it’s choosing the wrong data structure, taking on a too big of a task, or buying 20 euros worth of Ethereum on its ICO and forgetting the password.
The more mistakes we make, the more data points we have to learn from. If we never make mistakes and eventually stumble upon a problem that is too big for us we will feel devastated. A sheltered life cannot last forever, eventually an event big enough will come along and shatter that shelter. Our decision to thicken our skin before major mistakes is something that will help us a lot in those situations.
More senior people should also be aware that it is important to shelter people from what kind of mistakes they are making. Sometimes people are not ready to make a decision on some parts of the development. At that point the mistake is on higher ups letting that decision making process leak down to someone who wasn’t ready for that yet. Regardless of the situation, it is a good learning process for all parties.
Think before you act
Use common sense when developing
If something looks like it might be wrong, discuss and try to find a better way to do it.
In software development we are very aware of technical debt. At times we accumulate it, at times we try our best to pay off some of it. Because of this at times there are patterns in the code base that feel funky, that seem stupid or that are just plainly wrong. There might be hacks that have been done for a reason, because some restrictions have been in place when writing the code or because time constraints at the time have needed only a good enough solution.
These ‘patterns’ are not set in stone, they are in place because of external restrictions. If something like this comes up it is usually better to try to find the reasons behind decisions made in those ancient times rather than blindly copying the approach and going with it. These kinds of things stack up on top of each other and the longer they go, the harder it is to make the correction to a better solution.
Coming back to impostor syndrome, we all make mistakes and ugly hacks, before following those it is important to understand reason behind those hacks. Maybe the playing field has been leveled and a better path forward is available.
Understand cost of abstractions and cost of over-engineering [draft]
When we think of the software we are building we should consider it to be a living organism. Apart from idiotic rewrites, a software we are building usually lasts a ong time. It evolves over the years of its lifetime, sometimes having a fast, business driven phases, sometimes slower methodological technical phases.
In an ideal world we would be combining both of these phases while we are building our application, creating business value and maintaining high code base quality at the same time. Of course that is rarely possible and should be thought of when ever we are in the slower, more technical phase of the software life cycle.
Even if we might have more time to do some refactoring, introduce some patterns, add some abstraction to the application, it might not be a good idea. We have to think of the bigger picture in these cases. Some abstractions are smart, some are abstractions for the sake of appearing a smart developer. These abstractions might introduce more technical debt when the direction of the business changes. They might introduce overhead in terms of deployment processes, restrictions to application data flow or difficulties in onboarding new developers to the code base.
Much like chasing the shiny thing, over-engineering is one of the core needs of a human software developer. We want to challenge ourselves, try to find the perfect abstraction that will work for every single use case. Of course this is impossible but it is a very interesting problem and intellectual exercise for a software developer to try regardless. We as developers have to remember that we very rarely are library developers, business value is what matters in the end.
“First you learn the value of abstraction, then you learn the cost of abstraction, then you’re ready to engineer” -Kent Beck
Taking specific steps to get better
A personal note on what I have found helping me to become a better developer, or person in general.
Few years ago I started keeping a journal where I would every evening write down the things I did, learned and didn’t understand during the day. That journal has given me the ability to remember the journey I have taken and more importantly it has highlighted the path that I have found interesting during the year.
Every year ,after I have filled out a journal I review that full year and make plans for the next one based on that. This is a somewhat modified process that I have taken from Alex Vermeer and his 8760 hours planning structure. Having concrete goals and steps how to achieve those goals makes it so much easier to make adecision. The ability to know exactly what the wanted direction is will keep those thoughts on the surface and allow us to tweak our path towards the actual things we really want.
I really recommend to everyone to go through this exercise:
- Alex Vermeer, 8760 hours: https://alexvermeer.com/8760hours/
- Thinking, Fast And Slow - Daniel Kahneman
- Zen And The Art Of Motorcycle Mainteinance - Robert M. Pirsig
- Mindset - Carol Dweck
The actual reason it all exists
Lastly let’s think about the industry at large and why is it actually available for us to do the work we are doing. Without customers we wouldn’t be able to play around with that newest framework, we wouldn’t be able to architect those microservices nor would we be able to challenge ourselves with those fun, ‘genious’ level abstractions.
Getting the software out there wins every single time. We as software engineers have responsibility to
a) Actually finish our features and produce business value
b) Finish those features in a way that is forwards compatible enough for the software to grow.
That is not an easy undertaking, that is what makes this industry so challenging. Managing customer expectations and managing technical debt. Managing the timelines of shipping and managing the fear that ‘next person maintaining the code is a violent psychopath who knows where you live’. This duality is what makes me love the industry, I love what I do and I love the challenge of writing software that someone wants to use as well as writing software that makes me proud to have been part of building.
That is what gets me out of the bed every morning.
People to follow
- Venkat Subramaniam
- Kevlin Henney
- James Long
- Axel Rauschmayer
- Expert Beginner
- Dan Abramov
- Sebastian Markbage
- Martin Fowler
- Uncle Bob Martin
- Joel Spolsky
- Jeff Atwood
- Kyle “Aphyr” Kingsbury
- Stefan Tilkov
- Michael Lopp
Blog Posts & Articles to Read
- The Ten Commandments of Egoless Programming
- Things you should never do
- Amazon Leadership Principles
- 97 Things Every Programmer Should Know
- Give It Five Minutes
- Taxonomy of Tech Debt
- Mental Models I Find Useful
- What Is Code?
- Mythical 10x programmer
- Practicing Programming
- Mind Your Dependencies
Talks to See
- 12 ways to make your code suck less
- Declarative thinking, declarative practice
- Code Is the Easy Part
- Hammock Driven Development
- The Last Lecture
- Simple made easy
- The Future of Programming
Books to Read