What are mental models?
Before diving into the phases of learning and prioritization in software development, it’s important to understand the concept of mental models. Mental models are frameworks or cognitive structures that help us process information, make decisions, and understand complex concepts. They shape our perception, thinking, and behavior, making them invaluable tools for learning and decision-making.
Similar to how software engineers inherently use languages that have different programming paradigms to accomplish similar goals or programming capabilities (e.g. procedural programming, functional programming, object-oriented programming, declarative programming), different mental models enable individuals to solve problems and analyze challenges from different perspectives.
Simply put, mental models are tools used for thinking. Having a variety of tools helps you become a better thinker and problem-solver.
The phases of learning in software development
This blog post introduces Prioritization as a mental model for understanding the phases of learning in software engineering. As such, it is one of the first blog posts I share with my audiences, so that they can use it as a map to identify their current position and give them an idea of what directions they can pursue.
By the end of this blog, you can compare your current understanding of where you are in your career or profession with the mental model, and you can adjust your goals and directions as needed. I hope this helps you find success in various areas of life, and not just software engineering.
Understand it (the problem or the goal)
Before embarking on any learning journey, it’s essential to first gain a deep understanding of the problem or goal at hand. You need to understand what it is you are trying to accomplish, whether it is understanding what you want to do or understanding what problem you are facing.
You can trial-and-error your way by guessing whole numbers as answers, but understanding that the question is “2 +2” makes the problem solving a lot simpler. A well-defined question makes setting directions easy, and helps give you perspective on the challenge you are facing.
A lot of people don’t recognize how crucial this step is. As software engineers, we have a tendency to gravitate to “making things” immediately without fully understanding what it is we are trying to do. This is because we are creators or builders, and that’s our bias. We want to get straight ahead with tinkering and playing with some artifact or toy in front of us, and be able to show the world this new creation.
However, if we had simply flipped the script by first asking these questions, we could save ourselves from plenty of headaches, wasting time, and arguing with people around us because of misunderstandings:
- What does the world (market/my audience) need? or
- What problem is undeniable and pervasive in society? or
- What goal I want to accomplish? Why do I want to reach for this goal?
- Is the problem or goal defined clearly enough for my use? What are examples of too little detail and too much details?
Seek to understand your goals truthfully and clearly. Sometimes this means asking really difficult questions and not settling for anything but extreme alignment. The next thing is to pursue making things work (i.e. Do a Shitty Job).
Make it work
When I was a young developer, I merely typed what I wanted on Google and followed how-tos and tutorials online mindlessly, copy-pasting code to see if they would work. Sometimes it did, sometimes it didn’t. I didn’t expect it to work out on the first try because I knew that I was doing something that was way beyond me. That lack of expectations was, to some extent, a gift: I had managed my expectations of my performance.
So I would trial-and-error these problems and once I got it running on my machine, I looked at the code and the tutorial again, focusing this time back on “understanding it”. This time, I would focus on understanding the programming language, understanding its syntax and tweaking some parameters of the working code. A very simply example is changing a square’s X position from 50, to an X position of 100. Running it again showed me that I had influenced a change in my program. It was exhilarating to have this command of a powerful computer.
Once I learned the language to some degree, I explored how else I could get it to do more of the things I wanted it to do. The tricky part was making sure that introducing new changes doesn’t make previous code broken. This means that the code should not only work, but also it must be correct. That’s a big distinction especially for younger developers, who struggle to see that the failures they encounter with causing the program to break is part of the learning experience. It is an in-between step between making things work and making sure the program is correct.
Get the code running, run the code, see what happens, introduce changes, hypothesize why it broke, experiment, and repeat.
This incremental approach may appear overwhelming to others, but consider that the goal is not to achieve perfection or a correct program after 1 or 2 tries. The goal is to immerse yourself and gain experience! It is to get your hands dirty, to be familiar with the grime and the dirt and the broken things. It may be costly time-wise and effort-wise for someone who is starting to learn a new skill, but this set of failures is an important part of learning. You are moving from “I completely don’t know a thing” to “I’ve done a couple of things that are wrong, so I know which ways not to go”.
By creating a safe sandbox for you to practice, make mistakes, and learn, you will accelerate your learning by wielding it, tweaking it, and making it your own.
The code you are tinkering with will later on no longer be something you copied and instead you will recognize it as something that you can write yourself. It is the same transition when learning to say thank you in Japanese: At some point you are recalling “Arigatou” from your flashcards practice, but eventually, you recall saying “Arigatou” in conversation with real Japanese speakers.
Make it correct
As I mentioned a while ago, introducing changes to existing working software is not as straightforward as one might think. Sometimes changes causes a catastrophic failure. Sometimes, introducing new features actually causes us to realize that our initial understanding of the problem was faulty, or that there were hidden assumptions that our lack of experience failed to articulate or clarify. So we might dial back to previous steps of Understand It and Make It Work.
Over time, as we grow in experience, we learn to give fewer bold assertions or absolute or generalized statements. We learn to use hedging, and qualify our statements and say “it depends”. We learn to say this because of all the outlier cases, nightmare scenarios, and complexities. We realize that life isn’t black and white, so we need to clarify things by asking questions and understanding it very, very carefully. Yes. Step 1 was really that important.
As we continue to understand our problem or the challenges that lead to our goal better, we realize that the better we understand the problem, the more complex it becomes. More difficult, yes, but the more nuanced it is, the more we are also able to optimize and maximize opportunities because of the problem.
It’s the same thing with how fast-food restaurants were eventually designed. Entrepreneurs realized that not all restaurants are the same. Yes they sell food- but sometimes food quality or quantity is not the top priority of the customer: sometimes it is convenience and speed.
Complexity breeds new challenges, but also new opportunities.
Make it usable
Once we plateau at the skill level of software development where we are confident that we can make things correct, we find the next goal of pushing our craftsmanship in achieving optimizations and usability improvements.
Optimization can be done in various ways: data storage consumption, speed that a webpage loads, how quickly you can produce correct code, how efficient your algorithm is in terms of compute resources used, or how effectively you might be generating value for your company, or how well you are leading your engineering team.
The reason we explore optimization at this level is because “premature optimization is the root of all evil,” (Donald Knuth, the father of analysis of algorithms). Deciding to optimize code or applications only make sense once the application works as intended. What good would a functional application be (gives correct results), if the users struggle to learn how to use it, or if the data processing takes hours to complete?
At this point in your software development career, you might find that these nuances of algorithms and optimizations is the necessary greater challenges you need to vanquish to solidify your confidence in your field.
You might find yourself maxing out your individual potential contributor output and finding yourself at the same level of other peers who are also at the level of being able to put out correct code. Engineering management might come to mind at this point. I highly recommend you check out Sarah Drasner’s book, because she begins it with helping you understand your values.
A lot of engineering leaders worked to be best engineer they could be… and then we were promoted. It’s tough to transition roles. I wrote the book I wish I had when I started leading teams.Sarah Drasner, on Twitter, on her book Engineering Management For the Rest of Us
Make it pretty
Aesthetics is not something to be undervalued but I perceive it to be something to be pursued only after more important items were met. I studied myself on how I actually weighed which of these factors were important, and what I think would have been important to me, had I been a client requesting for a software to be built.
In my opinion, functionality (working and correct) comes first, followed by usability (optimizations), and lastly, aesthetics.
I would rather have something look ugly but get the job done rather than have an application that is very pretty but not useful or functional.
Later on, I realize that these activities were taking form based on what I perceive to be business value. There are outlier cases where the general guidelines I provided above will be invalid. For example, sometimes aesthetics is more important than functionality such as in the case of having a pretty marketing tool even if you still don’t have the full product yet.
Software Developer Apprenticeship
Where do you find yourself in this simplified model/spectrum of mastery in software development?
If you want to study along with others who are in an apprenticeship or are journeying to different places, feel free to send me a tweet or DM, or just say hi! I have a group of friends who are part of a Discord community of diligent and generous learners.
As always, thank you for reading.