This post was written by Bitbucket user Aleksandar Radulović.
With technology evolving fast, there is a need to write and maintain software more efficiently, and better communicate with team members. As developers, we rarely get to think about these things as we rush to meet deadlines.
Current software development practices rarely include software modeling. Even when models are used, they are mostly used as a part of the documentation process and often seem more of a burden.
The purpose of this article is to describe a different approach to software development that puts visual modeling and code generation into the heart of the development process. Visual software models put emphasis on communication and internal software design rather than simply making things work.
I'll describe how we use code generators to automate software development by using a UML model as a starting point for creating modern Java back-end applications using frameworks such as Spring, Spring Data and Hibernate.
In order to understand the potential of this approach, we need to consider different cornerstones of the development process and the impact this approach has on them:
- Unified Modeling language (UML) – used to visualize software systems and flows
- Modern Java – essentials of the modern Java ecosystem
- Building the Software Product – faster prototyping and maintenance
- Team dynamics – better communication and faster onboarding
Let’s briefly go through each of these.
Unified Modeling Language
The Unified Modeling Language (UML) is a standardized, visual language for modeling software. It was developed with an ambitious intention: to provide software teams a standard way to visualize the design of a system and to improve the team's understanding of the domain of the problem they were solving. Using UML, one can visually model concepts, processes, state machines, interactions or use cases.
The approach we take in our day to day work is to use class diagrams for modeling domain concepts and relations between them and state machines for modeling process flows. We also document different model elements: classes, interfaces, attributes, etc. so that we can derive documentation from the model at any time, using different formats and structures: javadoc or Swagger, just to mention the two.
Here is an example of a UML class diagram.
Modern Java has a vibrant ecosystem. While it takes time to learn a programming language, adopting modern frameworks from that language’s ecosystem is an additional learning curve.
The emergence of declarative software development practices has silently opened new ways for model-driven development. Unlike the imperative programming flows that are inconvenient to specify using modeling techniques, declarative programming constructs describe structural aspects of the software that can naturally be represented by the class diagrams.
Contemporary Java development heavily relies on declarative constructs: annotations most of all. For example, different frameworks, such as Hibernate and Jackson, use annotations to map object models to relational databases or to different export formats (JSON, XML, Protobuf, BSON, CSV). The Spring Framework, among many other things, brings great support for declarative development of RESTful endpoints and Spring Data introduces many essential constructs for abstracting data store access operations.
Like other types of programming, declarative programming does come at a cost – we introduce complexities of different frameworks and libraries into our applications. While these dependencies bring complexity to the project, developers must learn that they offer a return on developer productivity by letting them focus on high level objectives.
Declarative programming allows for code generation. Instead of having to write Java annotations by hand, it is enough to mark a class as persistent in the model and let the code generation tool create Java Persistence API (JPA) annotations for you. Instead of having to write lines and lines of JPA annotations, which can be cumbersome at times, code generation can do the magic without letting you bother with the details. Code generation is either built in to the UML tool you're using or may be available as a plugin – it's usually a one click process to go from UML to code.
Here is a sample of the Java code generated from the UML model shown above.
Why use code generators? Code generators translate the language of design (UML) into the language of implementation (Java). It brings automation to our development process, reducing overall development complexity and simplifying maintenance. We can be truly focused on modeling application concepts and services, the core abstractions we are dealing with, while the code generator synchronizes the model with the codebase. Further, it promotes the usage of best practices and significantly impacts the quality as well as the uniformity of the codebase.
When the model is completed, the code generator creates a complete starting project that reflects our design – so we can focus on implementing business logic. When it comes to software maintenance, you can change the design and let the generator propagate changes to your codebase. This process of working with a software model and using a code generator allows for rapid prototyping, easier software maintenance and gives you better documentation of your product.
The question that quickly arises when you start working with code generators is: how to synchronize changes that you introduce in the code with the model? Our answer to that question is simple: don't do that. The model is a set of abstractions and it should be kept separate from implementation.
This one-way transformation is typically referred to as the “model first” approach because it clearly puts emphasis on modeling and not vice versa.
On the other hand, we still want to be able to modify the generated Java code. For that purpose, we rely on preserved sections within Java source files, that keep custom changes intact through multiple code generations.
Development Team Now Has a Visual Map
Team development and communication are often underestimated topics in the everyday hectic run towards achieving results.
Using code generation brings the UML model to the heart of the software development process. The UML model of the product becomes a visual map that evolves as the work progresses. Having this map, different team members can understand the software better and have focused discussions. Onboarding of new team members is now much faster: instead of reading lines and lines of code, they rely upon a live map that communicates backbone ideas without implementation specific details.
This visual software development technique changes the traditional responsibilities of the team members, promoting mutual understanding of the domain and improving team cohesion. When using model-driven development, the role of software developer comes closer to the role of a business analyst. On the other hand, a business analyst clearly understands how the software is being built and the relationships between domain concepts. Finally, QA engineers have a better understanding of the application, and all team members speak the same language.
While it's possible to use code generation and modeling to automate parts of software development, we do not see this often in practice – either due to lack of awareness or lack of resources to invest in reviewing and researching new ways of working. If the ideas expressed in this article get you interested in model-driven development, there are several ways to go further.
There are multiple providers of low-code development solutions. Mendix is one of them and has a comprehensive guide to low code development.
The best open-source example of this category of products is JHipster, a project that has been embraced by thousands of developers worldwide. The JHipster core team managed to connect experts from different areas of software development to make this amazing application generator.
Our own endeavor is in extending StarUML, our preferred tool for software modeling, with a plugin for code generation – this is the plugin used in the example in this post.
Finally, no matter which tools and methodologies you use, software development is a people business and as such, it has many different sides that are difficult to measure and manage. Model-driven development cannot replace the lack of quality requirements, lack of empathy within the team, or lack of organizational culture in general. It complements agile development methodologies but does not replace them.
Author bio: Aleksandar Radulović is a software developer and architect who developed Rebel, a code generator plugin for StarUML. When he’s not developing software, he enjoys reading classics like Shakespeare or is dancing the tango. Connect with him on Linkedin.
Love sharing your technical expertise? Learn more about the Bitbucket writing program.