New Project Pitfalls for Beginner Developers

May 16, 2023
Josh Barton

Nashville Software School Web Development Instructor Josh Barton As an instructor at NSS, I guide many students through the process of planning and starting a new project. Web development students build two capstone projects and a variety of small group projects that more or less start from scratch. Project planning and setup is an integral part of the NSS experience, and it’s not surprising that planning and getting started present two of the greatest challenges that students face during their time with us.

During the planning process, it is important to identify a set of features that will constitute a working application (at least for the first iteration of the project.) Once these features have been identified and documented, our students turn those features into issue tickets on a project board, which are used to track their progress. Once on the board, the tickets need to be arranged in order of priority, and are then completed in that order. Ideally, the student developer only works on one issue ticket/feature at a time, and merges that code before moving on to the next feature in the queue.  

This process is not complicated, but it is still very difficult. Without project planning experience, established workflows, or an existing codebase, it is easy to fall into bad habits which hinder progress and cause problems down the road. One set of habits I often see beginners exhibit are all related to trying to build the entire application all at once, rather than building the application step-by-step, feature-by-feature. Good planning and adherence to the plan once development work starts are the best ways to avoid it.  

What does this phenomenon look like? Here are some symptoms that might indicate you've fallen into this pattern, and what to do instead:

Symptom #1

You find yourself writing feature tickets like "build a RequestsList module" or "Form for adding serviceRequest" that describe code rather than app behavior.

The biggest problem with this way of thinking about your code is that you are skipping the "what does my app need to do?" part of the planning process. Tickets that describe code rather than describe functionality usually include too many assumptions about how to solve the problem. The tickets need only describe the problem to be solved, and how to determine when the problem has been solved, not also solve it. Especially when other features in the app will have already been built when the developer works on this feature, presuming that certain code needs to be written to solve a problem is very limiting, and causes the author of the ticket to include the wrong requirements. 

Instead: Describe features in terms of user stories and acceptance criteria. You can use the “As a [kind of user], I [want to], [so that I can…].” and the Given-When-Then formats.

For example:
“As a service technician, I want to view the open service requests, so that I can see which requests are assigned to me and claim unclaimed requests.”

GIVEN I am a logged in service technician

WHEN I click on the “Service Requests” link in the nav bar

THEN I will be presented with a list of cards representing the open service requests

Symptom #2:

You find yourself "scaffolding" out all of your project's components when initially setting it up, but none of them actually work.

This is the “Frankenstein’s Monster” approach: build the body first (often out of spare parts), and then bring it to life afterwards. This is a very common mistake that beginners make, and comes with a number of drawbacks. First, it is very hard to debug this code. You have no idea why half of it is there (you were probably just guessing at what you were going to need for the components), so there are a bunch of vestiges from other projects throwing errors that you can't even find. Second, you probably did a lot of work that you don't need to do because, again, you were guessing at what you might need in the future rather than what you need right now. See: YAGNI (You Aren’t Gonna Need It).

Instead, try the following:

  1. Copying and pasting code from other projects isn’t always wrong, but has to be done carefully. If you are working on a similar project to something you have already done, only integrate code from that other project in small pieces. Confirm that the variables and names you are using for the component make sense in your current context, and test it to make sure it works before moving on to the next thing.

  2. Always notice when you are committing code, or considering a feature "done," when it is still broken! Try to only commit code that actually works (given current constraints like what's covered in Symptom #3.) When you consider a feature complete (and are therefore ready to merge the branch into main), it shouldn't have errors, and should be thoroughly tested for functionality before the merge. 

Symptom #3

You find yourself throwing a bunch of features in together with the feature you were supposedly working on, because the feature depends on those other features. 

First, it’s possible that you have prioritized your feature development incorrectly, and should have worked on these other features first. Careful attention is required during project planning to prioritize your work so that you are able to add features one at a time. 

Despite careful planning, sometimes it is not possible to completely avoid dependencies, but that shouldn’t stop you from working. Two ways to mitigate this problem are:

  1. If a component for a feature you are building requires data that is normally generated by users, make sure you have a well-seeded database preloaded with test data for the feature you’re building. Many students feel they need to create a form to add to the database before they build the components to display that data. Just create test data instead! It is much easier to start with a relatively simple feature like displaying items rather than building a form. 

  2. If a feature component you are working on requires a module that doesn’t exist yet, mock the other module with a simplified version that you can fully work out later. For example, you know you want to save images to a 3rd-party cloud service, but you're not ready to implement it yet. But the form you are currently working on requires the ability to save images! Create a module with a saveImage(image) function that just returns a url for a placeholder image location. You can use that function in your form, and then refactor saveImage later to do the real work with your 3rd-party library. 

Breaking all three of these habits boils down to this: You are building applications for people to use to solve business problems. Have their needs, and the functionality that goes with those needs, drive how you build your software, and build it such that you can test that it fulfills those needs every step of the way.

If you or someone you know are ready to start your journey towards a career in web development, discover how NSS can help you get there by checking out our upcoming programs!


Topics: Learning, Web Development