Published
10/21/2024
Categories
Web Design, Web Development, Software

Developer Best Practices

Here at Endertech we pride ourselves on doing a good job and providing maximum value to our clients. Feeling satisfaction in doing one’s job is what keeps work fun and interesting. In accordance with those ideologies, we pay close attention to our code quality. For me, I consider coding to be digital clay and a form of artistic expression. Along those lines, creating the best possible code can be considered an artform. There’s a lot to consider there; in this article I’ll go over the basic tenets that guide Endertech developers and our coding process.

The Benefits of Best Practices

So why do we follow best practices? There are a number of reasons:

Maintenance

Before we get into the best practices, let’s talk about the term “Maintenance”. In coding, this refers to returning to existing code and improving it in some way. This usually takes the form of updates, modifications/enhancements, and security patches. 

Maintenance is one of the main considerations for following best practices in code development. You want the maintenance to be as efficient as possible. Many times the person doing the maintenance did not originally code it. Their job should be made as easy and possible, they should be able to get up to speed with the code quickly and easily assess where their changes should be integrated.

Performant Code

Best practices can lead to efficient and well optimized code. It can also allow identifying bottlenecks and improving specific areas without disrupting the overall system.

Robust Code

Bugs are bad. By using best practices you can minimize the number of bugs.

Personal Satisfaction

As mentioned in the introduction, we consider this an art and it is very satisfying to create something that provides value to both the client and the artist. “In truth, whatever is worth doing at all is worth doing well, and nothing can be done well without attention.” - Chesterfield.

Best Practices

Below are a set of best practices, in no particular order, but simply in the order in which they came to mind. I’d say each is as important as any other, and together you’re going to have a quality result.

Analysis & Design

Before jumping in and coding, an analysis should be done. Define what needs to be done, and why. Only after fully understanding the problem can you effectively design a solution. An analysis will often reveal scenarios that were not considered, and other factors that may affect the problem and/or solution. You can also design your models, classes, and methods using the analysis results. Once you have a general blueprint of the solution, then you can start coding.

Another aspect of design is abstraction. Don’t design a solution that deals specifically with the exact scenario, but instead try to design the solution to be flexible so it might also solve other future problems, scenarios, or needs. To better conceptualize this, let’s look at an example:

Say a client has programmatically generated pages, and wants the ability to set meta tags on the pages keyed to the page URL. You could create a table named page_meta_tags, and then code it up to specifically write meta tags based on the URL, but first stop and consider the larger picture; do you think the client might only ever want to just set metatags for a page? Probably not, they’ll probably want to set other tags and content as time goes on. In general, the solution should be abstracted to allow setting ANY custom page content, not just meta tags. In this case the design change is actually relatively minimal since you only need to add a way to identify the content type, but now the solution is much more flexible.

Self-documenting Code

In general, it should be the developer’s goal to minimize the number of comments and have the code document itself. This is done by choosing variable and method names that accurately represent the data and processes involved. Ideally you should be able to read lines of code and have them read like sentences. You have nouns, verbs, adjectives, etc. that work together to construct blocks of code that tell you exactly what they’re doing and why.

Comments

There may be some circumstances where the full intricacies and nuances of the code can’t be fully represented using only self-documenting code. In those cases, code should have appropriate comments to convey anything a developer would need to know when working with the code. As well, methods should be documented using a standardized format. For PHP, it’s PHPDoc. This explains exactly what the method does, what arguments it’s expecting, the return value, etc. Most IDEs can also read these and provide hints when calling the method.

DRY

This is an acronym that stands for “Don’t Repeat Yourself”. This can keep your code clean and concise and also improve reliability. If you’re writing code and find blocks are doing the same operation on data or something very similar, you should break that code out into a method or function. The advantage here is you create a black box that performs an operation on something, and as well anytime that operation needs to be updated it will be updated for all places at once wherever it is used.

Efficiency & Optimization

Frameworks & Plugins

The simplest way to realize immediate benefits in this realm is to use a framework, plugins, and libraries as much as possible. If you have a problem, see if someone else has already created the solution. Not only does it save you having to analyze, design, and code something yourself, but it will be thoroughly tested and debugged because so many others are using it as well. In this day and age, you should be using a framework, no matter the size of the project. Another benefit is most frameworks are extendable and support a plugins ecosystem. So even if the framework doesn’t specifically do something you need, you might be able to extend an existing portion or plugin, or write your own and share it with the community.

Code Structure

At a more granular level, how you structure your code can save CPU cycles and also help with self-documentation. A classic example of this is with loops. Any variable that is not updated should never be assigned in a loop. Here is an example:

for ($i=0; $i<10; $i++) { $util = new Util(); $str = $util->transformString($str); }

This is a very blatant example, but you get the idea. That object should have been instantiated outside of the loop. Now you have cycles instantiating objects for every iteration. This might also throw off another developer, because they’ll first need to find out WHY this is being instantiated in a loop, only to find it doesn’t need to be. Bad stuff.

Google, RTFD, RTFS, and AI

If you’re not sure how to do something, Google it! 9 out of 10 times Stack Overflow will have someone who ran across your exact issue, and lots of people providing solutions and discussion on how to solve it. Many times I’ve often found ideas and solutions in developer blogs as well. Google really is your best friend.  

RTFD means Read the Friggin’ Documentation. Often you’ll find the best answers right there in the documentation for whatever you’re using. For frameworks and libraries this is essential. Bootstrap, Jquery, Symfony, API Platform, etc., all these sites have very robust documentation on how to use everything. Even frameworks and libraries have their own best practices, which basically led to how the framework was implemented and is used.

RTFS means Read the Friggin’ Source Code. If you’re using a framework or library and something’s not working or has bugs, don’t be afraid to dig into the source code to see exactly what’s happening. A problem will often be revealed once you understand what’s going on under the hood. As well, frameworks often use best practices themselves, so you might learn something new by reading other people’s code.

AI is a newer technology that can be helpful in development. From my experience, I found that AI is generally only helpful with very popular frameworks and languages. The more obscure the subject, the more likely the AI will confidently give you incorrect information. One example of this was with October CMS. I used chatGPT to ask a question about how to do something and it gave me an incorrect answer. After I determined the answer was incorrect, I told it as much and it acknowledged it was wrong, which I thought was odd. I then provided more details on the version and scenario. The AI then gave me another incorrect answer. After the third attempt and incorrect answer I gave up on it. But conversely, for well-known languages like javascript I am mostly provided with the correct answer the first time.

Copying & Pasting

There are some instances when a developer might copy and paste code from some other source into a project. Great care must be taken to ensure the copied code’s variables are appropriately renamed and comments are revised or eliminated as needed. Also, step through the code and verify it’s doing what it needs to do and doing it efficiently. You never want to paste and leave code you do not fully understand. You should go through every line and verify each is appropriate to the solution.

Copying and pasting entire files also requires great care. We inherited a project from a client where the developers copied the first model they created and used it as the basis for every other model in the project. Almost every model had the same useless methods and incorrect comments that came along from the source file.

Use a Debugger

This one could go under the Efficiency & Optimization section because it will save vast amounts of time in the long run, but it’s so fundamental to the development process that it deserves its own section. Debuggers allow you to step through every line of code as it's executed, see values in variables, paths taken, etc. In terms of troubleshooting, it’s invaluable. But it can also be used during general development to see what variables and objects look like during execution, what resources might be available, and so on. 

Code Readability

The following are some general guidelines for making code easier to read. Code that is easy to read makes understanding it much faster, and also is less bug prone since it’s easier to read and understand at the time you’re actually coding it.

Nested If Statements

The more nested if statements, the harder it is to read and follow the code. In general there should be as few nested if statements as possible, and no more than 3 levels. There are techniques for doing this, where the most common is to do each if statement separately and return if a condition is not met. Of course there may be another scenario where you can’t do that, so you’ll have to work out how to revise it without the nested ifs. If you’re new to coding, to get some ideas you can submit your code to an AI system and ask it to revise the code without nested if statements.

Lines of Code

In general, you want to keep the lines of code in each class and method as small as possible. Long methods are hard to follow, and also hard to keep track of and can lead to bugs. Break apart large methods into smaller methods. If a class has too many properties and methods, perhaps it can be broken down into smaller classes.

Code Formatting

Properly indented and formatted code is a lot easier to read. Determine which code formatting standard the project will use and stick to it. There are also tools that can format your code for you, but it’s probably a best practice in itself to hand-format it since that will force you to examine the code at the same time.

Error Handling

Add try/catch blocks to your code. Any caught errors should do two things: the first is to notify the user of the issue if applicable. The second is to record the error to a log. When notifying the user, make it user friendly. Don’t explain what was going on in the code, instead report back to the user that the action could not be taken due to an error and that it has been logged so the developers can look into it. No action should ever fail silently, nothing is going to make a user lose faith in the system faster than when they try to do something and absolutely nothing happens.

Logging

Most frameworks have built in logging and automatically log exceptions at the framework level. Make sure to add appropriate try/catch blocks in your code, and then also review that log at regular intervals to see what problems the system may be running into.

Code Review

You’re not done coding when you write your last line of code. Once you’ve completed everything you should do a self-code review and ensure all best practices have been followed. As well, now that everything is working together in a larger context, you may find some possible optimizations, refactoring, and renaming might be warranted. Once you’re done with your self-code review, you can then have a peer code review take place.

Testing

Depending on the complexity and project functionality, testing should be added as appropriate. Unit tests are mandatory for mission-critical systems. Examples would be systems that could cause harm if they failed, or systems that have financial impact. Unit tests and test driven development should be applied as needed to ensure a project’s code is robust and bug free. Scalability analysis and testing are also things to consider with website development.

Conclusion

In general, we’ve covered the most fundamental best practices, but as with any complex subject, there is always more to learn and study. This should be considered an introduction to best practices, and I’m sure you’ll develop your own additional set of best practices as time goes on and experience grows.