Clurgo logo
  • Clurgo
  • Blog
  • These high-octane coding tips will feed rocket fuel to your coding team

These high-octane coding tips will feed rocket fuel to your coding team

3/28/2023 Hubert Brychczyński

Share

Coding is an essential aspect of software development, and it requires attention to detail, consistency, and a willingness to continuously learn and improve. If you could give a few tips to your younger self that would help you avoid mistakes along the way, what would they be? We asked some experienced developers at Clurgo.

We are talking to:

Bartosz JędrzejczakSenior Java Developer, with Clurgo for nearly 4.5 years. Programs back-end web apps. After hours, he consumes books, films, and TV series – ideally set in a courtroom, e.g. Suits and Better Call Saul. He’s also learning the piano and enjoys windsurfing.

Kamil KorgaTeam Leader/Branch Manager in Lublin. With Clurgo for almost 8 years. Currently manages our branch in Lublin and doubles as a Team Leader in one of the projects. He regularly trains CrossFit and recreationally rides a bike. At night, he watches sports games and TV series or reads interesting books – mainly fantasy and sci-fi.

Michał SzymańczykSenior Java Developer. With Clurgo for a little over 4 years. Programs web applications. When not working, he does sports – mainly cycling and mountain climbing.

Michał ChmielarzJava Developer with many years of experience, currently a Team Leader at Clurgo for a little over a year. Loves good food, basketball, and books. A budding astrophotographer.

Tip 1: Fail safe, fail fast

Bartosz Jędrzejczak (BJ): In the project that I’m currently working on, we use an approach called fail fast. In this approach, we verify all the data right away to catch any potential errors and prevent databases from crashing. If an error does occur, it is immediately deleted. The advantage of this approach is that we can identify incorrect data at a very early stage – specifically, the moment the request is processed. This allows us to quickly locate the source of the incorrect data.
Naturally, fail fast comes with some disadvantages. First of all, fail-fast rules may turn out too rigid and require loosening if service analysis is imprecise. Furthermore, introducing new functionalities may cause the properties of certain code elements to change. Because of this, the application may respond with an error and stop the process. Therefore, fail-fast rules must be adapted to the new logic behind each change in the software, which can require a lot of work.
Michał Chmielarz (MC): And what about fail safe?
You could say it’s a mirror image of fail fast. The process is not stopped in response to data that the documentation does not account for, as long as the data remains technically correct. This kind of data may appear if, for example, we add new values to a set of variables.
Since the process does not stop, fail safe is a great choice when adding new functionalities. This is because fail safe processes new request types within a single service the same way it processed previous types, so it’s not necessary to change the code when they’re added. But even though there’s no need to change the verification rules after a major code modification, there is a side effect: the client may receive incorrect results or we may see a record in the database that makes no sense from a business point of view. On the other hand, fail fast identifies such situations because it runs individual fields in the request against business criteria. If a new request type is not supported, it produces an error.
In general, we can sum up fail fast and fail safe like this: we use the former when we know exactly what data we’ll get. If, however, we can’t know this because, for example, the data comes from a less precisely defined system, fail safe will be a better solution.

Defining the Request class:

private static class Request {
private String fieldA;
private List<Object> fieldB;

private String getFieldA() {
return fieldA;
}

private List<Object> getFieldB() {
return fieldB;
}
}

Fail fast method:

public void processFailFast(final Request request) {
if (request == null) {
throw new BusinessExeption("ERROR_BJ_20230125115703");
}
if (request.getFieldA() == null) {
throw new BusinessExeption("ERROR_BJ_20230125115814");
}
if (request.getFieldB() == null || request.getFieldB().size() != 1) {
throw new BusinessExeption("ERROR_BJ_20230125115831", request.getFieldB());
}

// processing request only if fieldA is filled and fieldB has only one element
// otherwise request is not valid
}

Fail safe method:

public void processFailSafe(final Request request) {
if (request == null) {
log.warn("Request was not processed - request is null");
return;
}

final String fieldAValue = request.getFieldA() == null ? request.getFieldA() : DEFAULT_FIELD_A_VALUE;

// if fieldA was not filled we can process request replacing it by default value

if (request.getFieldB() == null || request.getFieldB().size() != 1) {

// processing fieldB only if it has one element, otherwise skip field processing

} else {
log.warn("FieldB in request is not present or has more than one element - skipping fieldB [{}] processing", request.getFieldB());
}
}

MC: This all makes sense, but I also see a different option – contract-driven development, where it is the contract that determines what data is sent and expected.
BJ: Of course, a contract is a great solution, unless the system uses a canonical data model.
MC: Plus, contract-driven development is unfortunately quite uncommon.
Kamil Korga (KK): Another thing worth mentioning is the importance of properly logging all incoming data, because if we don’t log something, we won’t have a way of knowing where it came from and why, and we won’t react to it appropriately.
BJ: I agree. Logging is particularly important in fail safe, because in this model we are accepting data even if it’s incomplete. In case of problems, logs let us find out what’s wrong.
Michał Szymańczyk (MS): In one of my projects, the logs recorded each REST request, which enabled us to see the reasons behind an error, as well as to replicate it if necessary.
KK: We should also talk about the format of the errors and the content of the error messages. External systems should be able to read and understand both. If a customer gets a 500 or 400 error, you don’t know where it should go. An error-handling system helps in this case.
BJ: In my project, we assigned a unique identifier to every error found in verification. The ID was generated by adding a prefix and a time stamp to the initials of the person conducting the verification. When the client got an error message, we got the corresponding ID. This way, we knew exactly which verification caused the process to crash. We could identify the missing fields, the incorrect data, and the cause of the error without having to debug or analyze the logs.

Tip 2: Know your tools

MC: Knowing the functions of the tools you use gives reduces costs by making you faster and more efficient. This is especially valuable in emergency situations, like when an error pops up in production and you need to put out the fire as fast as possible.
You could use an IDE as if it were Notepad, depriving yourself of the many useful options it offers. For example, it’s useful to know the query language in GrayLog or keyboard shortcuts in IDEs. I myself had already forgotten how to navigate the IDE interface with the mouse, but a less experienced person refreshed my memory. I watched them working without using keyboard shortcuts. It took not two, but three times longer. Keyboard shortcuts can save a lot of time, which is extremely important in programming. A programmer is an expensive resource for the company, so we should make our time as productive as possible.
MS: I couldn’t agree more. I would also add that if keyboard shortcuts don’t seem to stick in your mind, you can use a plugin that gives you hints about them. Key Promoter X is a plugin that does that for IntelliJ. With such a prompter, you will remember the shortcuts after a while.
What’s more, the Promoter can see how often you use a function that doesn’t have a default shortcut assigned. It then prompts you to assign one.

Double Shift: Search Everywhere
Ctrl+Shift+A: Find Action
Alt+Enter: Show Context Actions
F2: navigate between code issues
Shift+F2: Jump to the next or previous highlighted errors
Ctrl+E: View recent files
Ctrl+Shift+Enter: Complete Current Statement
Ctrl+Alt+L: Reformat Code
Ctrl+Alt+Shift+T: Invoke refactoring
Ctrl+W: Extend or shrink selection
Ctrl+Shift+W: Increase or decrease the scope of selection according to specific code constructs.
Ctrl+/: Add/remove line or block comment
Ctrl+Shift+/: Comment out a line or block of code
Ctrl+B: Go To Declaration
Alt+F7: Find Usages
Alt+1: Focus the Project tool window
Escape: Focus the editor

Fig. 1: Keyboard shortcuts in IntelliJ (source: https://www.jetbrains.com/help/idea/mastering-keyboard-shortcuts.html)

MC: Also, if you have the Ultimate version of IntelliJ, you should know that it can be used not only for code editing or debugging, but also for database handling, connecting to the cloud, checking HTTP requests, and so on. This gives you everything you need as a developer in one window, so you don’t have to switch contexts and tools, which also has a positive effect on focus and productivity.
BJ: Another important fact is that IntelliJ gives us the option to view the source code of various plugins, which I wasn’t able to do in Visual Studio Code, for example. I’m not sure why, maybe it didn’t have this feature or the interface was too unintuitive to find it.
MC: Well that’s the thing, a good command of tools pays off. It’s worth taking the time to learn the more advanced options of our IDEs. At the same time, we need to keep up with the changes introduced in newer versions.

Tip 3: Go to the library

MS: We mentioned libraries. This is also a type of tool. Libraries are ready-made solutions to many programming problems that a developer faces in the course of their daily work.
Of course, you can try and code a solution yourself, but first of all, why reinvent the wheel, and second, you run a big risk of making a mistake while writing. That’s why you should check if the solution you need isn’t already available from Apache or Google.
BJ: Well, I think we should stress that you should only use trusted libraries, not the less popular ones. With the latter, the risk of some kind of vulnerability is higher. Also, don’t go over the board with the number of libraries – the more libraries we use, the more dependencies there are, and the greater likelihood of a vulnerability.
KK: Let’s keep in mind that there are scanners that check libraries for this. These scanners alert us not only in case there’s a vulnerability in a given library, but also that it has disappeared in a newer version. So the problem is gone.
On my part, I can recommend all the libraries from Apache Commons and Google, which we mentioned at the beginning. I’m also a fan of the Vavr library, which extends Java’s native capabilities with functional types derived from Scala.
MS: Libraries can also be used for testing. Instead of preparing data for testing manually, we can rely on a library that automatically generates the data.
BJ: And what do you think of Lombok?
MS and KK: Definitely recommend it.
MC: Lombok is controversial, though. On the one hand, it allows you to write less code, but on the other hand, it works at the bytecode level, which, depending on the version of Java (which has been changing frequently recently), can cause various conflicts.
BJ: What’s more, Lombok introduces an anemic data model to the project, which approach can cause problems in combination with the Rich Domain Model. This is because someone can use getters and setters and pull all the properties of a class out. So you have to be careful with libraries.

Tip 4: Test, test, test

MS: Before each pull request, we can use various tools for static code analysis. These tools will show us places that we would definitely not pay attention to when doing a code review with BitBucket. We can use scanners in Jenkins for this – for example, SonarQube.
BJ: SonarQube and SonarLint enable us to identify potential vulnerabilities in our code. This information helps us decide whether to increase the test coverage on our code or not. This has a great impact on code quality.
KK: Not only on quality but also on the number of bugs in all environments – in both testing and production. The higher the test coverage, the more stable our code and product are.
Testing tools are primarily useful for juniors because they quickly give feedback on what you have created. This makes it much easier for the rest of the team and team leaders to conduct code reviews. It’s much nicer to check code that someone has already run through Sonar.

Clurgo logo

Subscribe to our newsletter

© 2014 - 2024 All rights reserved.