DEV Community

Cover image for Be Strict in Software Development
Fırat Küçük
Fırat Küçük

Posted on

Be Strict in Software Development

BE STRICT

“Being strict” might not hold true in all areas of life: "Bend, so you don't break." However, I am going to talk about software development. Besides watching cat videos, it's kind of my only hobby.

Yes, “be strict” in software development. Draw lines, restrict users, restrict yourself. Until AI writes software for us, we need to be strict. Human nature is error-prone, and we tend to forget. Standards and metrics make us better engineers.

Also, our software quality fluctuates between mood swings. If you're not well-caffeinated or it's Monday morning, you might not be proud of your craftsmanship. I could list many bullet points, but let's stick to the 7 deadly sins of software development. Well, not deadly, but let's keep them in mind.

1. Constraints on Persistence Layer

Constraints on Persistence Layer

Your data is one of the most valuable parts of your software system. Imagine a user ordering -3 smartphones on your online webshop. You'd probably need to give those phones for free. Or imagine a user uploading a 1GB profile image.

Our persistence layer, in other words, our database, data warehouse, data lake, or whatever you call it, should be clean and consistent.

In some companies I've worked with, I've witnessed engineers intentionally avoiding the use of database constraints or foreign key relations because they might slow down write operations. Theoretically, that might be true, but the cost of inconsistent data could be much bigger, and the consequences might be really painful.

Another trend is "schema-less" NoSQL databases. In the real world, nothing is schema-less. You don't want to put your products in your user collection (table). In most cases, everything is well-defined. The only difference is that if you add some additional fields (columns), your database won't complain about it.

I can say they are mostly schema-flex (yes, I made it up) databases. Most of the time, schemas are our best friends—XML schema definitions, JSON schemas, or database schemas. Being strict about your data will reward you.

To sum up, be a good developer and use schemas, constraints, and relations if possible.

2. Data Validation

Data Validation

The internet is not a safe place from the very beginning of your website's life journey. People will try to enter the most ridiculous inputs you can ever imagine. Some users will do this on purpose, and for some, they were born like that. They will try to put emojis as usernames because who doesn't love unicorns, or they will try some JavaScript inside a textarea because JavaScript is their favorite programming language.

No matter what the intention is, the solution is restricting user input; otherwise, it might cause serious problems in your system. The most crucial part is backend validation because it directly affects your system. This input can be a form, REST API, GraphQL, or any other kind of protocol data. Also, data can come from the outside world, inside your company, or from another microservice.

Winter is coming. Prepare your backend. For better UI/UX, use frontend validation as well. Imagine you're filling out 5 pages of a form, and you realize you made some mistakes after sending the data to the server. Surprise! Form data is also lost. It is understandable that sometimes people want to beat up developers.

3. Separated Layers

Separated Layers

While developing, we have some logical boundaries. They can result from Domain Driven Design or a software architecture you have followed. If your code is inside a single file with 6000 lines, it might not be a good indicator.

Separating your business logic and transactional part might be a wise decision. I assume I first saw it in a Quarkus project. It allows you to create some architectural layer communication tests. For example, when we call the repository method directly from the controller layer, these layer tests will fail.

Modern IDEs have powerful tools for checking coupling and cohesion between packages, layers, and namespaces. Also, some automated plugins check that kind of layer testing.

4. Security Hardening

Security Hardening

Most of us have some insecurities, but that doesn't mean we aren't going to help poor apps rise and shine. Probably, this one should be a whole separate article. But I can mention some of the security issues here.

Forcing users to choose strong passwords is the first step that should be taken. If your system stores passwords in plain text, please don't. Also, we may want to use one of the modern hashing algorithms like SHA512, SHA3, and a teaspoon of "salt" against dictionary attacks.

Taking action against code injection might be another important step. Modern frontend libraries encode HTML, and some ORMs prevent SQL injection by default.

Enabling CORS, using CSRF tokens, using strict HTTP headers (CSPs), a TLS layer, constant dependency scans, and automated vulnerability scans can be some of the important ones.

5. Code Quality Check

Code Quality Check

Code quality check starts with a good code style. Using a code formatter and format checker can be a first step; a linter might also help against some redundant or faulty usages. Like using "var" instead of "const/let" in JavaScript or an unused import statement in Java.

Some plugins or helper services might help you check your code status. I use such tools for limiting the line count of methods and classes and restricting the cyclomatic complexity of a method or a class.

Some other tools might prevent you from shooting yourself in the leg. They can detect well-known anti-patterns and faulty usages. Also, we can put testing itself into this category: unit, integration, smoke, end-to-end API/UI tests. Code and path coverage also might be good indicators of the status of your application.

Some frameworks also provide strict modes for better usage checking and best practices. Using those kinds of options might help you improve code quality.

Better to put those checks in a pipeline or a git commit hook; it might help us keep our code base clean.

6. Cross-cutting Concerns

Cross-cutting Concerns

Let's assume we aim to persist a simple User entity in the database. In ideal conditions or a scenario, every user can provide perfect inputs. That code could be a couple of lines. However, we need to pay attention to input validation, database transactions, auditing, timeout management, database connection pooling, retry mechanism, exception handling, anti-corruption layer, and so on.

Programming becomes a cumbersome task if you're dealing with lots of boilerplate code. It could be 3 lines of code or a thousand lines of code if you're dealing with that kind of thing in your actual code. Using modern frameworks might help with that one. Or some kind of interceptors, middlewares, or aspect-oriented programming.

7. Statically-typed Language

Statically-typed Language

I don't want to start a flame. This might be a sensitive topic if you're emotionally attached to your programming language. Most modern languages use better practices compared to their predecessors. Statically typed languages help us see our coding errors beforehand or provide us with better IDE or editor code-complete support. They might offer us to compensate for the billion-dollar mistake "Null" data type. Modern languages provide syntactic sugars for optional data management. Also, by default, immutable data types are a really good way to handle data.

If you're using JavaScript, TypeScript might be a good alternative (and avoiding the "any" type). Kotlin can be an alternative to Java. For Python developers, Python already offers static typing in version 3. Using that one might be a wise choice.

Top comments (0)