A few weeks ago I was looking through our team’s backlog. The top ticket was filled out something like this:
Title:
Update this really awesome API with a new field for a really cool feature.
Description:
Add the field “email” to really-awesome-api-service, so that we can expose it for editing to the user.
Technical notes:
Add the new field to the Data Object
You might need to update the database
Update the controller to support CRUD operations on the new field
Write test cases
I’ve seen much less detailed tickets. In fact, this one was pretty good in my experience.
So what’s the problem? What kept me from just doing the thing?
I didn’t know how to do it.
I went to the GitHub link for the source code. I perused it for a bit and realized it was a Kotlin service.
I don’t know how to program in Kotlin.
I knew almost nothing about this application, including the tech stack it was built on.
Spring? Maven? Hibernate?
I have barely touched any of them.
Not only that, I had no idea what database needed to be updated. Or, as the ticket hinted at, if it even needed to be updated.
There’s a lot I didn’t know.
So, I wrung my hands, gave up, and cried in the corner, while rocking in the fetal position.
Just kidding…
As engineers, this is exactly what we’re paid to do. Encounter something we don’t understand or know. And figure it out.
So here’s how I figured it out.
First, I started with what I did know:
I knew this was a REST API, with a database behind it for persistence. While I haven’t worked in this kind of tech stack before, I know the concepts from studying and working in similar applications.
The ticket mentioned a Data Object. I know that’s a pretty common pattern for ORMs (Object Relational Mappers). It would make sense that I would have to find and edit this part of the code.
I knew that Spring uses the MVC pattern for web APIs. I understand how that pattern works, so it gives me a frame of reference as I navigate the code.
I’ve worked in .NET, Ruby on Rails, and a handful of other frameworks implementing essentially the same thing. At the end of the day, it involves making migrations for the database to the new schema, updating the models and entities that talk to that database, then updating the controller to provide the methods to view and edit that information. There might be some nuance, and some middleware to take into account, but those are the bones of this kind of task.
So I had a rough idea of the big picture subtasks, but what I didn’t know was…
How do I make the migration to add the new field (changes to the underlying database schema)?
Are these migrations stored in scripts anywhere?
Is there a special command I need to run to do those scripts? or sequence of commands?
What database is this connected to?
Do I need to directly connect to this DB and make any changes there, or can it be done completely from the application code?
Are there any other tables or relationships that need to be accounted for? Any dependencies I could break in the process?
How do I even set up and run this project?
So how did I begin to answer these questions?
First, I searched through the code for anything related to the data object. I found the relevant code, and lo and behold, it had some fields I recognized. It seemed simple enough to copy the code from one field and modify it to suit my needs.
Then, I searched for an example field name throughout the entire project. For our purposes, let’s say I used the field “FirstName” to base my work on. I found all references to “FirstName,” and this painted a picture for me of all of the bits of code I’d have to amend for my new use case.
Now, I had to figure out what database the app connected to. I knew from experience that this is usually defined in some kind of configuration file, and was able to find that.
Next, I looked through the project’s README to see if there were any instructions on setting up the project. There were, so I proceeded to get the app up and running on my machine.
Now, I had what I thought was all of the relevant code updated, and I had the project set up. However, I still couldn’t figure out how to migrate the data to the new schema. This is when I resorted to reaching out for help.
I looked through the project's commit history and found the person who most recently did work there. I shot a message to that person.
I couldn’t get any further, so I took a two-hour break and scrolled through Instagram until he could respond.
I’m just kidding again.
I didn’t want to let that block me, so I kept digging and trying to think of how I could get this information.
Then it hit me…
This seems like a pretty typical task that’s probably been done before… There’s probably an old pull request similar to the work I’m doing now. So I searched all of the closed PRs for the text “add field.” And sure enough, several results popped up.
I found a good example and it highlighted to me a few things I did right, and some things I had overlooked.
I still didn’t have everything I needed though. I still needed help. So I amended my questions to my coworker with the new information I had. I was able to ask more detailed, explicit questions, and he was able to give me much more direct, simple, and higher-quality answers with this new information.
We weren’t ready to activate on the work yet. So, I updated the ticket with a summary of what I had learned. That way, the next engineer to take on the work would start from a much better position.
As I reflected on my day, I realized something. I do this all the time.
There’s a pattern here.
The following is my best attempt to abstract what I did into a step-by-step process:
Start with what you know.
Identify what you don’t know.
Craft the right questions to ask to learn what you don’t know.
Figure out an order that makes sense to ask those questions in. Some answers to questions are dependent on answers to previous questions. There may be an inherent order to work in.
Ask those questions via whatever methods are available…
Read through the source code.
Read through the documentation.
Review language and tooling documentation.
Search Google.
Ask ChatGPT 😜.
Ask a human being.
Get direct help by pairing with a person.
Repeat the process, refining the details until you have clear action steps to take that get you closer to solving your problem.
Solve problems one by one. As you solve, new unknowns will likely reveal themselves. Apply the same process to figure these new problems out.
Fundamentally, this is the essence of what I have done as an engineer for my entire career. Repeating this process and variations of it as I learned more and encountered new, more challenging problems.
As you advance in your engineering career, the problems you tackle will be less well-defined, more ambiguous, and more challenging across several dimensions.
But the fundamental process of problem-solving remains the same. If you master that process and build those problem-solving muscles, you’ll unlock the ability to tackle more interesting, difficult problems.
And you’ll get to experience the rewards that come with solving them.
Have fun.
I get tasked with things outside my immediate knowledge bank all the time.
My goto solution tree is similar;
Do I understand the end goal?
No? - converse until so
Sell yourself as an innovator as opposed to a guru.
What is it similar to?
What are some specific tools that will be used?
Watch videos on those tools.(as they may cover the desired project indirectly)
Bid high enough for 2 attempts (or 1 attempt and specialty tools)
Be open to "breaking even" on time in exchange for experience.
27 years of that later, I have a shop full of trade tools and the expertise and experience to sell competent service in multiple fields.