This post covers how to avoid errors before running the application, explains what compile-time safe is and how to use it, and example with Java full-stack application.
Join the DZone community and get the full member experience.Join For Free
Compile Time Safe Code Is True Gem In Development World
Compile Time Safe Code is the code that has a guarantee that after compilation it will work as expected. Such code is valid by design. Some languages and frameworks give Compile Type-Safe (not Time) solutions but this article covers more than just Compile Type Safetiness. Keeping your code Compile Time Safe reduces the number of potential bugs and makes code more reliable.
Compile Type-Safe (Not Time Safe)
Compile Type Safeness gives a guarantee that all variables are assigned to values with the correct type. Meanwhile, Compile Time Safe code is a more general notion that assures developers that code is not just type-safe but consistent in general.
So as far as we don't have compile step, our bug will be revealed only at runtime (when it's too late to fix it)
Java has Compile Type Safetiness
Now let's try to write the same code but in Java:
So in the second case, we can identify the problem even before running it. This practice gives us a huge advantage, many bugs can be identified even before running or testing them.
Applying Compile Time Safe Practices In Java FullStack App
Let's review how we can apply the Compile Time Safe approach for classical full-stack applications. In most cases, Java Web applications are represented by 3 layers:
- JVM (Spring/JavaEE/Plain Java + external services)
- Database Integration: JDBC queries, ORM (Hibernate, etc)
- Use TypeScript (I personally recommend)
Let's write our buggy code and confirm that TS finds that bug:
As an alternative to TS, you can use Flow Framework (backed by Facebook) that can highlight type inconsistency by adding @flow annotation on top of your JS code. Let's see how it work for our code:
Integrating SQL Databases with Compile-Time Safe Approach
Now we integrate SQL databases but not just in Compile Type-Safe way but Compile Time Safe. Java has many solutions for database integrations from Jdbc to Spring Data. So which of them provides a Compile Time solution? The best example - JOOQ. Before we start, let's write compile not safe solution using Jdbc:
As you can see in the example there are at least 3 potential threats that might be discovered only during runtime:
- Query with mistake
- Wrong column name
- Wrong column type
So one problem is related to inconsistent types that we might read from the results set. Another problem is a possible syntax mistake in the query.
Making JDBC Calls in Compile Time Safe Style Using JOOQ
In order to make all queries compile-time safe, we need to create an OOP solution that might validate the query before it reaches runtime. JOOQ framework provides a query builder that can create queries based on the database schema. Such a builder is compile-time safe. Let's introduce the schema and database and generate JOOQ entities used for query builder:
Selecting one single as UserRecord
Selecting all records as a list of Pairs
Comparing Plain JDBC request and JOOQ
JOOQ solved all problems we had in plain JDBC requests. Meanwhile, JOOQ Builder is flexible as any plain query. JOOQ is a good example that shows that a big majority of problems can be sorted even before compilation.
Compile Time Safe Is More Than a Set of Frameworks
Is there anything else we can do? Yes, we can. The idea of code safetiness is not limited by some framework or language. Each developer has the power to write code in a safe style. The only question is how much effort it requires.
Solving A Problem Using Compile-Time Safe Approach
Let's solve a problem by using the Compile Time Safe approach. In the next example we deserialize data from the received HTTP response (responseText object):
So during deserialization, we can't know for sure what object is returned in positive and error cases.
What we can do with it? As a solution, we can generate a TypeScript class returned from Java API. Let's review how to do it in the next chapter.
Code Generation Way to Achieve Compile Time Safe Code
In some sense making compile safe code means to limit available instructions only by valid options. One of the known ways is to generate all valid instructions even before we write the code (In the JOOQ example we also had a generation step).
Generating TypeScript Object according to exposed Java API
To have valid parsing we take Java Objects returned by backend and generate corresponding TypeScript classes. We can create our own generator but to simplify it we can use the existing typescript generator. Now let's review our Java API:
In the controller, we return 2 types of objects. For normal cases, we return the User class. For exceptional cases (if the user's name is shorter than 3 chars) we return a User Exception object. To run generator start maven plugin:
As a result of the execution we have the next typescript objects:
Now let's use generated interfaces and make deserialization valid:
Having safe parsing we can launch our code with valid and invalid users:
The source code of this example is available on github in this project.
Code Generation Using GRPC
As an alternative to the previous, a bit artificial example, it makes sense to describe a much more popular and useful technology - GRPC backed by Google. GRPC provides a way to write classes universal for many languages like JS, Java, Python, C#, etc.
Having a proto file you can define a contract between any client-server communication between any mentioned languages. For example, the next proto file decides the format for the Greeter service with HelloRequest and HelloReply.
GRPC communication has now become pretty popular in the enterprise development world due to its performance and multi-language "compatibility".
The Carelessness of Unsafe Code
According to my personal opinion, I find that one of the reasons for the big number of buggy codes is the exception-based approach. Now in most languages, we have such a notion as Exception. By design, it had to stand for abnormal behavior but in practice, it's used as just one more returned value. I believe that such an approach deprived programmers of the responsibility to write predictable and safe code.
Human consciousness, by its associative nature, has replaced the concept of exceptions by code with errors. Just take a look: NullPointerException is top #1 according to OverOps.
The top 3 exceptions say that developers didn't take care about validation or didn't follow the type-safe code approach.
People are not perfect beings and we tend to write code with mistakes but it's necessary to think twice about code safetiness and as a consequence code quality. Here I provided just simple examples as food for thought. So as a developer you can find your own ways how to achieve safe code.
Opinions expressed by DZone contributors are their own.