Vineet Sinha sees the same situation playing itself out in various enterprise organizations across industries. Productivity is down, the codebase is too challenging to maintain -- much less expand -- and developers are spending more time in meetings than doing their jobs. While he might not be able to change the need for meetings, Sinha says simplifying the codebase and bringing productivity back are both likely outcomes of improving Java modularity in applications.
A recipe for Java modularity
Sinha is co-founder and chief technology officer of Architexa, where he leads from the front, mentoring the development team, managing product architecture, overcoming performance hurtles, and integrating various systems. He presented a wonderfully informative session on modularizing large legacy Java applications for Java developers and architects at EclipseCon 2013 in Boston.
In his presentation, Sinha gave enterprise architects four steps that will increase modularity in legacy Java applications.
Step 1. Build architecture maps
An architecture map is important for getting everyone involved in the project on the same page, regardless of the take one has on Java modularity. Different individuals involved in the process will understand the main structures in the codebase differently. This goes for different departments -- development, QA, ops and business -- as well as different developers within the same department. "If you ask 10 different developers to draw a picture of the codebase, you're going to get 10 different diagrams," Sinha pointed out. "Separated development teams could refer to the same structures by different names. This confusion could potentially escalate into major disagreements that hinder teamwork."
When done right, the process of creating baseline architecture maps can be done in a single meeting that could last only an hour or two. In small teams, a core developer might be able to draw up a starting architecture map on his own. This will give the developer a place from which to start when discussing changes with others. In larger organizations, it's important for all the stakeholders to be represented. Sinha recommends getting all the team leads together in one room. "Have them actually draw it," he said. "Get the main things down on paper."
Step 2. Merge architecture maps
Once developers or team leads have their own separate view of what the code looks like, it's time to start making compromises among them to get one overarching picture. A successful modernization project will require a single, easy-to-find document that gives all stakeholders a common, mile-high view of the existing architecture. This map will allow them to have more collaborative -- rather than argumentative -- conversations about what must be done.
If there's a high degree of disagreement about which components do what and fit where, start by taking only the things everyone does agree on. Focus on the top-level big components. Develop a group understanding of what those components are. Spell out the connections among components. Find duplicate features that are running separately under different names. This process will point out places where relatively small changes -- like merging the nearly identical "trash" and "delete" functions -- can streamline the codebase and bring the team closer to a maintainable, modular application portfolio.
Step 3. Relocate existing code
Developers can give themselves a little celebration at the halfway point, Sinha said. If the development team has completed the first two steps successfully, they now have a much better understanding of the existing system and what it needs. The team also will have a more robust shared vocabulary for talking about systems integration issues. Now it's possible to move on by organizing the existing code more intelligently.
For example, Sinha said, there might be a client-side filter and a server-side query that do the same thing. "Once the team understands the hooks," he explained, "they can migrate to a shared naming and make the filter and query work better together."
With a solid architecture map in hand, enterprise architects can sort and organize the code. At this point the team should be ready to move files closer to their final locations. This is a huge step forward, according to Sinha. "The problems start taking care of themselves," he said. "When the code is more organized, the bugs stand out and are easier to understand."
Step 4. Break bad dependencies
This is an important step, and it requires a context and dependency injection expert. This task can't be delegated to an inexperienced developer, according to Sinha; it takes a lot of experience. It is important to fix naming conventions to ensure consistent component application programming interfaces. Naming things the right way is an important part of cleaning things up. It's important to keep in mind that certain steps along the way should be kept private.
Building the skills to really get this part right can be difficult, but is well worthwhile. Sinha advises that senior developers practice coding dependency injection and inversion of code without a framework on simple projects or hypothetical codebases. "It's not too hard to do yourself," he said, "and it will get you very comfortable with doing it in tight spaces in your codebase."
When it comes to your actual codebase, be very conservative about making these changes. Start at the most surface level and work your way deeper into the code, layer by layer. Keep in mind the difference between compile-time dependencies and runtime dependencies. This can be particularly important with database dependencies.
Apart from dependencies, there are other specific tasks that will require specialized attention. Classloader challenges, for example, are their own discipline, according to Sinha, and require a dedicated expert developer. "The code can be a complicated mess hiding a crumbling architecture," he warns. "Making changes can be a slippery slope for inexperienced developers." He suggests having experienced members of the team lead coding talks to share their experience, and enforcing code maintenance with automated tests and code reviews.