Identifying gaps in your knowledge

14 Sept 2021

At the end of the inimitable Julia Evans’ excellent post ‘Get better at programming by learning how things work’ she says:

Someone who read this post asked me “how do you figure out what you don’t know?” and I didn’t have a good answer, so I’d love to hear your thoughts!

As someone who has relatively recently become a software developer and is still in the dark about a plethora of topics both technical and otherwise, I have spent a good deal of time trying to grapple with my own formidable ignorance. There are of course a near infinite number of programming-related things that I do not know – that’s one of the best things about this job – so finding something isn’t the problem, the problem is identifying and capitalising on opportunities to convert things from unknown to known – to learn. Over time I’ve noticed a handful of behaviours that prevent me from learning, and from even realising that I’m missing a chance to learn something. These behaviours help me feel safe and competent, but they are a lie.

Jump to heading Hubris

Overconfidence can be a real barrier to figuring out what you don’t know, and someone just starting out with programming can be particularly susceptible to it because you are often hungry for signs of your own success and any evidence of increasing control or mastery (see ‘Rise of the Expert Beginner’).

One example I ran into recently was another team’s service throwing Redis maxmemory errors that were causing downstream issues in production. “That’s an easy one,” I said to myself, “I’ll just bump the memory requirements until the other team can take over”, which I did – the alerts stopped and I felt pleased with myself. A couple of hours later, though, the errors were back and the service was consuming all of the new memory I’d just provisioned for it. My heart sank as I fed the hungry beast another gigabyte of memory to quieten it. Was I looking at a potential memory leak? Why was the keyspace constantly growing in size? Was this an indication of some kind of malicious user abusing the system? I had gone from what I thought was a simple solution to a known issue to a world of infinite possible problems, all equally likely and tricky to debug.

As is so often the case with human beings, however, my mistake was based on a series of faulty assumptions. I thought giving the application more memory would help the situation but it was in fact making the problem worse. In our particular setup Redis will actively keep its own memory usage below the maxmemory number by deleting stuff that hasn’t been accessed recently (see ‘Using Redis as an LRU cache’), but it only starts doing this when it gets close to the memory limit. Me adding more memory was preventing it from doing its job! I had seen a memory-related error in the logs and assumed it required a similar triage solution to other memory-related errors I’d seen in the past. This faulty pattern matching happens all the time and can often send you down avenues of exploration that are completely fruitless. Where you really should have started is with your base assumptions.

Knowing when you’re making assumptions can be extremely difficult, how do you distinguish an assumption from hard-won, deep understanding? In my experience the best developers are constantly working through and challenging the things they believe to be true in any given situation, verifying that their understanding is correct and applicable for this particular case. To me it’s part scientific method, part humility. You may not ever fully understand how a certain thing works, and that’s ok because abstractions are what help us leverage other people’s work and make us more productive, but assuming that you understand something fully is a tempting and dangerous trap to fall into. Always question your knowledge, validate it, because your underlying mental model is never going to be wholly complete. This process helps reinforce and deepen your understanding over time and is, I think, a crucial skill to develop.

Jump to heading Laziness

If you’re anything like me you’ll be fortunate to be working with people who are smarter than you, and you will regularly come across things in their pull requests that you don’t understand. This can be anything from a piece of language syntax you’ve never used before, to an unfamiliar pattern or a completely new piece of technology. But you’ve got your own work to be getting on with and a couple of other people have already approved the pull request, so you don’t really need to dig deeply into it and you don’t want to delay anything by withholding your approval, so you type the dreaded ‘Looks good to me!’, press the button and get on with your day.

It’s easy to do, and we’ve all done it, but there are obviously problems with this approach. The main one for our purposes is that you’ve missed the opportunity for learning that code review is meant to give you; knowledge of the change hasn’t been spread properly throughout the team and if this code breaks in the future and you have to fix it you’re almost certainly going to be at a disadvantage.

This happened to me once with some extremely clever list reordering logic that someone on my team had implemented. We needed a way to be able to reorder an arbitrarily long list without changing every item in that list every time one item in it was moved. So if you have items A, B an C each with an index of 1, 2 and 3 respectively, how can you reorder it to B, A, C without changing the index on all three items? The solution was deceptively simple: if you want to move A between B and C then A should have an index that is the average of the index values of B and C. You only need to make a change to A; both B and C remain untouched! I was still very new at the job, painfully aware of my own ignorance, struggling with nearly everything. This pull request was just another in an increasingly long list of incomprehensible things and I didn’t dig deeply into it. Eventually my brilliant colleague moved to a different company and we discovered a bug in the logic that I then had to try to fix; how I wished I had taken the time to understand his changes while he could have explained them to me!

It’s completely expected that you’ll come across things that you don’t immediately grasp in your colleagues’ code, but instead of shying away from your ignorance or relying on others to understand it, try to embrace it. Look up that piece of syntax in the docs, you might not need to fully comprehend 100% of what it’s doing to develop a deeper appreciation for the change itself, it might be enough for now to get a sense of what it’s trying to achieve. Not sure why someone has chosen a particular pattern for their code, why not ask them? In my experience most people are all too happy to explain the nifty solution they’ve found to a problem! If it’s a completely new piece of technology then maybe you can set up some more dedicated one-on-one time with your colleague to walk through it together. Ignorance is a constant state in our profession, and the sooner you can become more comfortable with it, welcoming it rather than avoiding it, the sooner you will reap the benefits.

Jump to heading Fear

Most developers have probably experienced a reluctance to work on at least one piece of code or business domain at some point in their careers – a code repository that has a reputation for being hard to work in, say, or deals with crucial business logic where making a mistake can cost the company money, or maybe it’s written in a different framework or programming paradigm that you’re not familiar with. If your team is large enough you might get away with being able to avoid these codebases for a time, and if your colleagues are especially diligent then you may not even need to approve pull requests for it, but you’re disadvantaging yourself by playing it safe and staying ignorant, not to mention being unfair to your co-workers. Personally I have come to realise that apprehension and reluctance are extremely reliable indicators for something I should do (at work!). Things that make you feel this way will push you out of your comfort zone and help you grow.

This happened to me early on when I was becoming comfortable working on a collection of applications, and even starting to get a little bored. I had an opportunity to join a team that worked on authentication, which was a big scary unknown to me at the time; I didn’t know how any of it worked and the thought of making a mistake that might cause our users to be unable to log in was very stressful! I took the job and it was as hard as I had expected. The codebase was large and complex; it interacted deeply with a web framework and a couple of equally complicated open-source packages; and my mental model of authentication was immediately revealed to be woefully naive as I realised just how many authentication methods were at play. Every ticket took me an eternity to complete and I felt like I was back at square one; I needed to spend hours and hours reading through the third-party packages, diagramming their internals to help me understand how everything fit together; and I had to read books about OAuth and OIDC so I could start to correct my mental models. Eventually I was able to add a completely new authentication pathway for ‘Sign in with Apple’, and when there were bugs with it I was able to fix them.

It’s been a couple of years now and I continue to learn. I still don’t have a complete understanding of all of our authentication pathways, but I know enough to be able to work through most problems. Importantly, though, the process gave me confidence that even if thrown completely into the deep end with a task or a problem I know I can work my way out of it; I have, after all, done it before!

Jump to heading Conclusion

There will never be a shortage of things for you to learn, that’s a given; if all you want to find out is what you don’t know then you can just open random Wikipedia article pages with a high degree of confidence. What’s more important to me is being able to recognise the level and limit of your understanding of a topic, and to constantly challenge and verify that knowledge because it will never be as complete as you might imagine it to be – often the things you don’t know are the things you think you know well! Equally vital is not to shy away from things you know you don’t understand, either because you tell yourself you’re too busy or you’re afraid of feeling out of your depth. These things are likely relevant to your work now, will be in the future, or are opportunities for you to grow. Dive into these unknowns, see where they take you, I promise you won’t regret it.