Introduction
To preface, I'm a core maintainer at The Odin Project, an open source, self-directed, online programming curriculum aimed at beginners looking to code. We help thousands upon thousands of learners in their programming journey yearly.
A frequent issue we often see our learners have is that they become overwhelmed with starting the projects throughout the curriculum. We start learners off with very specific instructions for the earliest projects, giving them specific variable names and functions they should have in their code, but as they progress through the course we step away from that and allow them to figure out the implementation of required features with instructions that become more vague the deeper they get into the course.
This mirrors a real life work environment. As some of you may know, as professional software engineers, we don't always get the full implementation of the required work in our tickets. For those who aren't yet there, a “ticket” sounds just like what it is, a ticket that describes the work that needs to be done, whether it’s a feature or a bug fix.
Most of the time at my job, those tickets tell me what needs to be done, but rarely how, unless I’m looking at an implementation spec for a new feature, which tells you the variables, functions, and fields expected so everything can work together without engineers just patching together a bunch of random code with no coherent style and structure. Even with implementation specs, there is still a ton of room for interpretation for an engineer. In short, most of the time a ticket will simply say what needs to be done, with a completion criteria that broadly outlines the expected behavior for the feature or bug fix.
So without explicit instructions, how do I actually tackle the problem at hand?
Read The Entire Specification First
The first thing I do is read ALL of the requirements carefully, I don’t start with hacking away at the first completion criteria and then work through the rest of the criteria. I need COMPLETE context for the entire ticket so I don’t code myself into a corner because how I implemented the first criteria may contradict the fifth. New learners may think that it makes sense to work through a problem step by step, but the goal here is to gain a high level understanding of the entire job at hand.
For example, I might get a ticket like this.
Title: Resume Upload in XYZ service
Description: Add functionality that parses and uploads candidate resumes to S3, and adds the parsed candidates to the job.
It’s very bare bones, and tells me what needs to be done, but not necessarily how. That’s up to me as an engineer to implement. In this case, in real life I may have an implementation spec which adds more detail about expected functions and schemas, but for the sake of the example we'll keep it simple.
Breaking It Down
This barebones explanation in the ticket is great though, because there are multiple things that need to be done here.
- Parse resumes
- Upload resumes
- Add candidates from resumes to the corresponding job
This gives me a great point to start at as far as writing out my pseudocode. They’ve already given me the general steps for what needs to be done. My pseudocode breaks this down even further. For our self directed learners at TOP doing these projects (or any time you have a personal project and you're stuck trying to actually implement a feature), you have these broad steps and you need to break it down even further to be able to effectively begin to solve the problem.
Pseudocode!
After understanding the scope of the work and what should be accomplished, I move onto looking to solve the problem. From here, I need to pseudocode before touching any actual code. For me, this means using comments in my file(s) to write out "instructions" for each piece of my feature or the solution for my bug fix. There are no rules to pseudocode! I use comments so it's easy to code along (more on that later), but pen and paper works, or your note taking app of choice works too!
As mentioned, I write down each individual step of what this piece of the application should do in plain English. I’ll possibly write the function names (which are not set in stone at this point) and add comments inside of those functions which describe very broadly what they should do. I am not worried about ANY code at this time, except for having in the back of my mind what something could look like. I don’t want to confuse myself with the details quite yet.
/* processResumes.js */
function uploadToS3(){
// uploads to S3 bucket
}
function parseResumes(){
// iterates over each resume
// uses parsing library
// creates candidate for job
}
function submitCandidates(){
// adds candidates to job
}
I broke down some of the steps into separate functions (which are not set in stone, they could be broken down more as I develop the feature), and I’ve written what they should do. I’m still not touching any code, I’m focused on tasks at this point. I’m not super focused on implementation itself, aside from a general idea of what functions might be needed and what they might be responsible for doing. But the key here is breaking things down into pieces in plain English (or your language of choice). I use "might" because at this point, the implementation is still shadowy and subject to change as I do more research into the task at hand.
I Broke It Down -What If I Don't Know How To Accomplish This Task?
Now, I have no idea how to upload anything to S3. Never done it before, but the following steps would equip me with the ability to learn. As a professional developer, I like to say that my actual job is reading and researching, not coding, and that my role is "problem solver", not "programmer".
My first order of business is probably going to be the Amazon S3 documentation. Any time I know I need to use a tool that I'm unfamiliar with, the first instinct is to check the docs.
Now, that might not pan out as my solution because my job might have a helper utility that does a lot of the heavy lifting for me. I will help me understand the tool better though! If that's the case, I'll look around for internal documentation about how we upload to S3. As a learner, unless you're using a library, you don't need to worry about this, the official documentation is king.
After 30 minutes to an hour, if I’m stuck and I’ve looked on StackOverflow, looked at our code base to see where else we upload to S3, and checked Slack for prior conversations about the subject and I just don’t get it, I’m going to ask questions to get unblocked.
Of course, in your personal projects and as a learner you don’t exactly have this luxury of internal documentation, an existing codebase, or Slack, so you might just jump to asking questions to get unblocked after doing your own research. No shame in asking questions, across my organization no matter how experienced an engineer is, they are asking questions likely daily.
Ask Questions
The goal of asking a question is to clarify where you’re stuck, and get unblocked so you can move on. I go into asking questions with an end goal in mind, remove whatever blocker I have in my understanding, not just of the work at hand so I can move forward. Before I type up a question, I frequently refer to this article to help formulate a question. Sometimes I find that as I follow these tips to form my question, I answer it myself!
From there, ideally I ask my question in the Slack (for a learner at TOP, this may be our or another Discord community), get it answered, or ask clarifying questions if it’s still unclear. Then I ask for resources if I’m still stuck on a broader concept. If I’m no longer stuck, then I get to the actual code! From there I translate my English pseudocode into real code.
Finally, let's write some code!
Let's consider the parseResumes()
function above.
function parseResumes(){
// iterates over each resume
// uses parsing library
// creates candidate for job
}
In my pseudocode, I mentioned that I need to iterate over every resume. I can easily correlate that "instruction" to a loop. After, I know that I need to create a candidate from the parsed resume, so I know I will need to create a Candidate here and pass in the info from the parsed results. This gives me a good foundation to begin writing actual syntax.
function parseResumes(resumes){
/* I know I'll have multiple candidates from multiple resumes
so I choose to initialize an array */
const candidates = []
// iterates over each resume
resumes.forEach(resume => {
// use parsing library
const results = parsingLibraryMethod(resume)
// creates candidate for job
const candidate = new Candidate(results)
candidates.push(candidate)
}
return candidates
}
Notice how writing my code alongside my comments helps inform what syntax I should be using? I tell learners that planning your code in this way is a requirement before you touch any syntax. We need to know where we're going before we can start driving, right?
Conclusion
Of course, things are subject to change as I work through the issue, but using all of my resources and planning out a high level overview of what my code should be responsible for helps me ultimately be more productive and helps me figure out problems I may ultimately not understand at a first look at the requirements.
TL;DR, read all of the instructions first for context, ask questions and do some research if unfamiliar with how to approach a problem, and pseudocode in plain English (or your native language) what your app should be doing in order to break it down further.
Photo by ALAN DE LA CRUZon Unsplash.