How to a novice developer to avoid rampant debugging, red eyes and spoiled mood
Hello! I am a technical officer and coach of Binary Studio , so I work a lot with students and novice developers. All of them make similar mistakes that slow down professional development. In this article, I gathered some tips on how to avoid cones in June. This article will be useful primarily to those who have already tried to write commercial projects, but still do not have enough experience. I tried to concentrate solely on practical examples, avoiding unnecessary reasoning. All material is based on my personal experience, so if you have anything to add, write in the comments.
Use ready-made tools
If I could return at a time when I was just starting my career, one of the most important tips for myself would be the following: use tools! As I remember now, I wrote a term paper, the task of which was to get an image from the camera, process it with a set of algorithms, write some data to the database and display the finished picture on the screen. And it’s all in real time. The most difficult thing was to debug such a program: it was necessary to simultaneously monitor errors in working with the camera, the correctness of the algorithm, accesses to the database, and, of course, working with the UI framework (whoever worked with WPF styling will understand me). Over time, having filled a few cones and participated in the creation of a number of web projects, I developed for myself a set of principles that can help cope with this complexity.
Like any self-respecting June, I undertook to solve the problem as a whole: in order to make up the page, I had to create a web application in Visual Studio from the template, find the desired page from the list of existing files and write random HTML / CSS, as the result can be see in the browser only after pressing F5. If it was necessary to send a request to the server, I added a button to the page, hung a handler on it, added the logic for building the HTTP request, pressed F5, and only after clicking the button could I see if I correctly described the request handler on the server. Visual Studio was my entry point to the application and exit point. Often I did not even understand what happens when I press F5, because the goal was to solve a particular problem. Is that familiar?
Divide and rule
Being a coach of the Binary Studio Academy, as well as checking dozens of homework a season, each time I come across the fact that novice developers make the same mistake: they are trying to cover the complex task as a whole instead of breaking it down into small components.
This strategy for reducing task complexity by dividing it into simpler subtasks is known as divide and conquer. In this case, there are two assumptions that are usually true:
- the task can be divided into several parts so that each part can be solved independently;
- the solution to each of the parts that make up the task is less complicated than the solution to the whole problem, and in this way we can “defeat” it.
Layout and frontend
Now, if I need to write a UI, I do not start the server. Instead, I open an online editor (for example, Codepen) and make up the components I need. If I need to edit an existing part of the application, then Chrome allows you to edit HTML / CSS on the fly using the Elements tab in Chrome DevTools. After that, it remains only to copy the final version and paste it into the application (this is much easier than restarting the application after every slightest change). In addition, DevTools allows you to debug JS (not with alerts, but with the help of transitions), set breakpoints, track variables and read Call stack. Separate extensions make it possible to simplify work with JS frameworks (Angular, React), monitor application status (Redux) and performance.
Communication between client and server
If I need to send a request, I no longer create a Test button on the form. Instead, there are several tools that describe HTTP requests in full detail: cURL, Postman, Fiddler, RESTClient. I like the latter most of all, since it is an extension to VS Code and it allows you to describe all requests in the form of an interactive text file that is easy to manage. If I need to more carefully analyze network traffic, then you can turn to Fiddler or Wireshark. That way, I can focus on developing a specific API method at some point in time, not paying attention to the rest of the application.
Business logic development
If we talk about the business logic of the application, then unit tests come to mind first. Instead of launching the entire application, unit tests allow you to focus on a specific function that can be called with one or another argument. VS provides a convenient interface for running and debugging tests, and you can use one of the following frameworks to write them: xUnit, NUnit, MSTest. It is not necessary to achieve 100% coverage with tests, it is enough that you can verify the correct operation of your code without running the entire application. I deliberately did not say a word about debug, because, it seems to me, this is the first thing that novice programmers encounter. But, in addition to debug, I also advise you to deal with the disassembler and learn to read someone else’s code (packages, libraries).
Do not forget about the data. ORM in every possible way try to hide work with SQL from the developer. You write a model, then a couple of library function calls and get the necessary selection. But the more complex the system, the more complex this model looks and the greater its number of functions. Therefore, arm yourself with tools for working with the database in advance: IDE, profiler, query structure analyzer, log analyzer. If you work with MS SQL, then most of the tools come out of the box (MS SQL Management Studio, SQL Server Profiler, Execution Plan Analyzer), for other databases you have to google each tool with keywords.
It is very important that you not only split the application into separate components, but also in those places where you defined the “seams”, describe the stubs: mock data, scripts with HTTP requests, SQL queries. This can be useful for two reasons: firstly, so that you can always test an isolated component, and secondly, when combining all the parts, you have a contract before your eyes that you must follow.
Before summing up, I would like to give an example of developing a specific feature (user login), using everything that I listed above:
- First of all, I focus on the UI. As mentioned above, I open CodePen and try to draw the interface of the component that corresponds to the mockups (two fields for entering the login and password and the Login button).
- When the UI is ready, I can transfer all the code to the component and create all the necessary handlers. At this stage, I replace all the services for working with these services with stub objects in order to test the operation of the button without accessing the server.
- After all operations on working with the form (clicking on the button, validation, error messages or switching to another page of the application) work correctly with stub objects, I can switch to the backend. First, I describe the endpoint that the model accepts and returns the 200 code. Using RESTClient, I describe the HTTP request in a text file (address, headers and request body) and check that my server responds correctly.
- Before starting the business logic, I’ll look into the database and check if all requests related to authorization can be executed on existing data. Since I usually use ORM, this step is just a hedge, because ORM itself will build all the queries, and I only need to describe the model correctly.
- Now I can start the business logic. I will not go into details of the implementation of the authorization functional, I just want to say that at this stage the correctness of your algorithm is easiest to verify using unit tests. Having ensured that my authorization function worked correctly, I can connect it to the database, call it using the API method and check the whole bunch using the same text file with the request.
- When the “API + logic + database” link works, I can replace the services with stubs on the client with real requests to the server and, if all the contracts were described correctly, I automatically get the working functionality. It is worth noting that until the last step, I focused on a small section of the program each time and only at the end I launched the entire application.
Of course, this approach is imperfect and has its flaws. First of all, discipline is very important here. You always want to take it all at once. It is important to learn how to divide a large task into several components and focus on each of them individually, discarding everything else for later.
Secondly, bugs will not go anywhere, but this approach provides more opportunities for control at each stage. If, for example, you see that your server is returning the wrong data (using the Network tab in the browser), you can switch to the text query file and not use the browser until the server is working correctly.
Thirdly, you may have noticed that during the development process I had several additional artifacts: mock services with stubs, a text file with HTTP requests, unit tests and database queries (which are also desirable to save in separate files). In the future, you will have to support all these artifacts. Of course, you can always get rid of additional files as soon as you finish developing a specific feature, but I highly recommend not doing this. It is much better to constantly build yourself this kind of “forest”, so that at any given time there is something to rely on.
Finally, most likely, you will not be working on the project alone, so it is advisable that the whole team follows a similar approach. Otherwise, someone will cease to support additional artifacts or completely delete them as unnecessary (see paragraph above).
And yet, remember the main thing 🙂
When I started my development path, I could not find a similar article that would push me in the right direction. It cost me countless hours of rampant debugging, red eyes, and a spoiled mood. Watching current students, I often see how they step on the same rake: they spend a lot of time on already solved problems, instead of concentrating all efforts on the subject area, and delegate secondary tasks to tools. And even if the approach described above is not applicable to your case, and writing an additional code is a burden, remember the main thing: use tools!