· updated · Thomas Karner
From Activiti to plain Java: when a process engine stops paying off
We moved three BPMN processes off an outdated Activiti engine and reimplemented them in Java. A field report on approach, effort and lessons learned.
In one of our systems, three business processes – among them the archiving of deeds – ran as BPMN models on an Activiti engine. The version in use had aged, and upgrading it would have taken considerable effort. So we faced a choice: modernise the engine, or implement the processes directly in code?
We chose the latter. This post explains why, how we went about it, and what we would do differently next time.
Starting point
The three processes were simple from a business perspective. Even so, they depended on a complete process engine with its own database schema, its own configuration and its own dependencies.
In production this caused two problems. First, process instances occasionally failed and could not be resumed cleanly. Second, debugging was tedious: to understand why a process was stuck at a particular step, you had to jump between the BPMN model, the engine tables and the Java code.
An upgrade would not have changed much. Current Activiti versions are built on Java 17 and Spring Boot 3 (Activiti 8.0.0 release notes) – for us, that would have been a major migration just to keep three simple workflows running.
When a process engine makes sense
We are not against process engines in principle. They play to their strengths when
- processes run for a long time and include human tasks, deadlines or escalations,
- many systems, departments or people are involved,
- workflows change often and the business side is meant to maintain the model itself,
- traceability and monitoring of individual instances are essential.
With a few short, stable processes, the balance tips the other way. Operation, maintenance and upgrades then cost more than the flexibility the engine gives back.
Many teams are weighing exactly this right now: Camunda 7 Community Edition received its final feature release, 7.24, in October 2025, with no further releases or security patches after that (Camunda, 2025). If you are affected, it is worth asking honestly whether you need a new engine – or whether plain code would serve you better.
How we did it
1. Understand the entry points. We first traced every REST call that starts a process and identified where results are returned to users. That told us where the new implementation had to plug in.

2. Consolidate the activities. The existing Activiti code was spread across many small methods, several of which belonged together. We condensed roughly ten individual steps into three cohesive methods. This made the code easier to follow and became the basis for everything that followed.

3. Reimplement without the engine. We then implemented the three methods with no reference to Activiti. The process state that the engine used to manage through process variables now lives in a dedicated table. Queries such as createTaskQuery() were replaced by ordinary JPA queries using Hibernate.
4. Repeat and clean up. We repeated this for all three processes and refactored the code at the end.
On a small scale, this is what Martin Fowler describes as the Strangler Fig: the new grows step by step alongside the old until the old is no longer needed.
Effort
The migration took about a month in total – longer than the processes’ simplicity suggested. Most of the time went not into writing new code, but into tracking down behaviour the engine had quietly taken care of, and fixing the bugs that resulted.
What we learned
Planning saves time. At first we went straight into the code. We got lost in details, and when something broke it was hard to tell which change had caused it. Smaller, clearly scoped steps – each with a test – would have saved us a lot of time early on.
An engine does more than you think. Timeouts, concurrent process instances, resuming after an error and restarting from the last successful step: Activiti provided all of this, and we had to rebuild all of it ourselves. If you replace an engine, write that list out completely before you start.
Conclusion
For our case, replacing the engine was the right call. The processes are now easier to read, test and debug, and an entire dependency with its upgrade path is gone. This is not a general argument against process engines – but it is an argument for questioning their use regularly.
Sources
- Activiti: Release 8.0.0 – Activiti & Activiti Cloud Developers Guide
- Camunda: Camunda 7 Enterprise End of Life (EoL) Extension Announcement (February 2025)
- Fowler, Martin: Strangler Fig (2024)
- Object Management Group: Business Process Model and Notation (BPMN) 2.0