What we build
Enterprise Java, written to be maintained
Java earns its place in systems that have to keep running for a decade. That is also why most Java work is inherited rather than started, and why the first skill required is reading somebody else's code honestly.
Spring services and APIs
Spring Boot services, REST and messaging interfaces, authentication and authorisation, and the contracts between them written down before the code.
Interfaces are versioned from the first release. Retrofitting versioning onto an API with live consumers is the expensive kind of avoidable work.
Services a second team can call without asking questions.
Application server migration
Moving applications off WebLogic, WebSphere and older JBoss onto Spring Boot with an embedded container, including the vendor-specific code that made them portable in theory only.
JNDI lookups, container-managed transactions, proprietary deployment descriptors and the classloader tricks are dealt with individually rather than swept into a rewrite.
One less licensed runtime under the application.
Java version upgrades
Moving from Java 8 or 11 onto a supported release: module system consequences, removed internal APIs, changed defaults, and the dependency tree that resists all of it.
Upgrades run incrementally with the suite green at each step, because a single jump on a large codebase leaves you unable to tell which change broke what.
A runtime that still receives security updates.
Integration and messaging
Kafka, JMS and file-based interfaces to the systems around the application, including Oracle and Microsoft estates we already support elsewhere.
Consumers are idempotent and replayable. A message delivered twice should be dull, and on most inherited systems it is not.
Interfaces that fail loudly and recover without a manual fix.
Persistence and performance
JPA and Hibernate mappings, query behaviour, connection pooling, caching, and the N plus one problems that only appear with real data volumes.
Performance work starts with measurement against production-shaped data. Tuning against a small test database produces confident nonsense.
Query behaviour you can explain, not just observe.
Taking over an inherited codebase
Build restored, dependencies inventoried, a test harness put around the critical paths, and the undocumented behaviour written down as it is found.
The first deliverable is usually a repeatable build on a clean machine, which more inherited Java projects lack than anyone expects.
A codebase a new engineer can change without fear.