01. Executive Summary & Core Insight
Software rarely fails outright when it is poorly built—it simply becomes progressively harder, riskier, and exponentially more expensive to change.
Organizations routinely invest millions rewriting legacy monolithic applications into distributed microservices architectures with the implicit assumption that "modern" architecture automatically produces "cleaner, maintainable" code. This empirical study by Dr. Amit Puri, conducted for his MS in Data Science at Liverpool John Moores University (LJMU), treats the monolith-to-microservices migration as a controlled natural experiment to test that foundational assumption.
Microservices decomposition successfully eliminates structural "God-Class" pathologies (plummeting encapsulation flaws, cyclic coupling, and broken inheritance by ~80%). However, it does not fix developer discipline: cognitive smells like Unutilised Abstraction surged by 117% (191 → 416) and Long Statements rose from 318 to 385, while Magic Numbers remained virtually identical.
02. Research Problem & Guiding Questions
Prior academic software engineering research frequently studied monoliths and microservices as isolated domains using disparate metric definitions. This left a crucial empirical void: no direct, metric-calibrated side-by-side comparison using a unified static analysis pipeline across both paradigms.
This study formulates four anchoring research questions:
RQ1: Quality Degradation During Migration
Does software quality actually degrade as organizations invest in cloud modernization and architectural rewrites?
RQ2: High-Sensitivity Metric Shifts
Which specific code-level and architectural metrics shift most dramatically when moving from monoliths to microservices?
RQ3: Friction as a Driver for Low-Code
Does the frictional overhead, cognitive load, and operational churn of microservices rewrites nudge enterprises toward No-Code/Low-Code platforms?
RQ4: Metric Evidence for Platform Adoption
Which code maintainability and complexity metrics quantitatively make the business case for adopting low-code platforms over custom microservices?
03. The 11 Microservices Bad Smells Taxonomy
In monolithic systems, code smells reside comfortably within package boundaries. In distributed microservices architectures, smells metastasize across network boundaries, becoming dramatically harder to detect with conventional class-level linters.
The research synthesizes the literature into the definitive 11 Microservices Bad Smells Taxonomy:
1. Wrong Service Cuts
Services sliced along horizontal technical tiers (UI, Logic, DB) rather than bounded business domains, causing chatty distributed anti-patterns.
2. Hard-Coded Endpoints
Services directly addressing physical IP or DNS addresses without dynamic service discovery, DNS rotation, or load balancing.
3. Cyclic Service Dependencies
Circular invocation loops across services (Service A → Service B → Service C → Service A), introducing severe distributed deadlock risks.
4. Shared Persistence
Multiple supposedly autonomous microservices reading and mutating the exact same relational database tables, breaking data encapsulation.
5. Greedy / Mega Services
A single oversized service masquerading within a microservices cluster, consuming disproportionate compute and business logic.
6. Missing API Gateways
External client apps directly invoking internal microservices without rate-limiting, SSL termination, or centralized authentication.
7. Excessive Protocol Standards
Uncontrolled sprawl of disparate communication protocols (gRPC, REST, SOAP, WebSockets) across the same service mesh.
8. API Versioning Pitfalls
Breaking contract changes deployed without backward compatibility, requiring high-friction synchronized lock-step releases.
9. Inappropriate Service Intimacy
Services bypassing public API endpoints to access internal private classes, caches, or shared storage abstractions directly.
10. Shared Distributed Libraries
Coupling all microservices to a monolithic shared utility JAR/DLL, causing upstream dependency lockouts upon every minor patch.
11. ESB Overuse & "Smart Pipes"
Burying business logic and transformation pipelines inside enterprise service buses rather than keeping endpoints smart and pipes dumb.
04. Empirical Methodology & Extraction Pipeline
The study deployed a comparative analytics pipeline using standardized static analysis across two complementary datasets:
| Dataset / Dimension | Composition & Repositories | Analysis Tooling & Thresholds |
|---|---|---|
| Monolith Baseline: Qualitas Corpus | 12 benchmark Java monoliths selected from the Qualitas Corpus (112 systems, 754 versions) ranked by GitHub stars and LOC: Hibernate, Spring, Tomcat, Derby, Lucene, ANTLR, Apache Ant. | Designite static quality analyzer:
• Method LOC > 100 • Cyclomatic Complexity > 8 • Class LOC > 1000 • LCOM (Lack of Cohesion) > 0.8 • Fan-in / Fan-out > 20 |
| Microservices Corpus | 18 curated open-source Java & C# microservices repositories, evaluated alongside their Dockerfiles and Kubernetes YAML manifests as first-class architectural artifacts. | cloc/sloc for line counts, custom R statistical scripts (process-cs-data.R, curate-data.R, analyze-data-func.R), and automated shell extractors. |
Magic Number appeared ~182,000 times across monoliths and was isolated from the primary comparative index to avoid swamping fine-grained structural signals. Conversely, Unutilised/Unnecessary Abstraction (~136,000 occurrences) was intentionally preserved because it served as the most decisive indicator of speculative over-engineering in microservices.
05. Longitudinal Aging & Compounding Technical Debt
Tracking projects across historical release cycles revealed that technical debt does not accumulate linearly with code size—it compounds exponentially:
| Project & Version Window | Codebase Size Shift | Observed Code Smell Trajectory | Architectural Takeaway |
|---|---|---|---|
| Apache Ant (v1.1 → v1.2 → v1.8.4) |
~6× LOC expansion over lifecycle |
• Unutilised Abstraction: 67 → 135 • Broken Hierarchy: 13 → 41 • Deficient Encapsulation: 16× Jump • Long Parameter Lists: 17× Jump |
Technical debt is non-linear. As legacy monoliths age without dedicated refactoring budgets, structural smells compound at nearly triple the rate of LOC growth. |
| ANTLR (v2.4.0 → v4.0) |
Major architectural rewrite |
• Complex Method: 127 → 80 (Refactored) • Long Statement: 46 → 318 (Spiked) |
Deliberate architectural refactoring successfully reduced method complexity, but introduced long method chaining and statement sprawl. |
| Microsoft PowerToys (Modern Monolith ~57 KLOC) |
Modern C# / C++ codebase |
• Complex Method: Low (Double digits) • Long Method: Low |
Monoliths are not inherently bad. Modern monoliths equipped with rigorous CI/CD static linters maintain exceptionally clean profiles without microservice complexity. |
06. The Quantitative Comparison: Monoliths vs. Microservices
When the monolithic benchmark dataset is compared directly against the microservices corpus, code smells divide into three unambiguous behavioral categories:
| Code Smell Metric | Monolith Baseline | Microservices Corpus | Empirical Variance | Architectural Diagnosis |
|---|---|---|---|---|
| Deficient Encapsulation | 187 | 37 | - 80.2% Drop | Smaller service cuts naturally limit public class and field leakage. |
| Cyclic-Dependent Modularisation | 146 | 31 | - 78.8% Drop | Service boundaries force decoupled compilation units. |
| Broken Hierarchy | 168 | 39 | - 76.8% Drop | Microservices favor composition over deep inheritance trees. |
| Complex Method (CC > 8) | 80 | 11 | - 86.3% Drop | Focused endpoint handlers curtail multi-branch method sprawl. |
| Unutilised Abstraction | 191 | 416 | + 117.8% Surge | Over-engineering: generic interfaces created for single implementations. |
| Long Statement (> 100 chars) | 318 | 385 | + 21.1% Increase | Fluent builders, stream pipelines, and inline serialization chaining. |
| Magic Number | 267 | 226 | ~ Flat (Neutral) | Architecture-neutral smell driven purely by individual developer habit. |
07. The Infrastructure Dimension: Docker & Kubernetes Smells
A major contribution of this research was treating Infrastructure-as-Code (IaC) as code. While monoliths have minimal infrastructure definitions, microservices rely heavily on Dockerfiles, Helm charts, and Kubernetes YAML manifests.
Dockerfile Smells
Unpinned base image tags (:latest), bloated image layers without multi-stage builds, running containers as root, and un-cached package installations.
- Root user execution
- Unpinned apt/npm dependencies
Kubernetes YAML Smells
Missing CPU/memory resource limits, absent readiness/liveness probes, hardcoded plaintext secrets, and unconfigured pod disruption budgets (PDBs).
- OOMKill risks from missing limits
- Missing health check probes
08. The Bridge to Low-Code & Citizen Development
Under RQ3 & RQ4, the research investigates why so many enterprise microservices migrations end in cognitive fatigue:
Building and operating 50+ microservices requires managing distributed tracing, circuit breakers, service meshes, Kafka topologies, and container orchestration. For standard business CRUD applications, this massive cognitive tax yields negative ROI. This frictional overhead is the exact catalyst driving enterprises toward Low-Code / No-Code platforms (Power Platform, AppSheet, OutSystems), which abstract distributed plumbing while exposing clean API contracts.
09. Actionable Guidelines for Architects & CI/CD Linters
Never assume decomposing into microservices buys cleaner code. Configure your automated CI/CD static analysis pipelines with these calibrated rules:
{
"rules": [
{
"key": "UnutilisedAbstraction",
"severity": "BLOCKER",
"description": "Fail build if interface has only one concrete implementation without extension rationale"
},
{
"key": "MaxStatementLength",
"threshold": 100,
"severity": "MAJOR",
"description": "Prevent unreadable fluent chaining in stream operations"
},
{
"key": "SharedPersistenceBan",
"severity": "CRITICAL",
"description": "Disallow cross-service direct SQL schema queries"
}
]
}
- Target Unutilised Abstractions: Fail builds when interfaces or abstract base classes have only a single concrete implementation without an immediate extensibility rationale.
- Enforce Statement Length Limits: Restrict chained fluent statements and long stream filters to prevent unreadable, bug-prone one-liners.
- Mandate IaC Static Analysis: Integrate tools like Hadolint for Dockerfiles and Kubeconform / Checkov for Kubernetes manifests directly into the commit pipeline.
- Enforce Shared Persistence Bans: Automated architectural linter checks must ensure no microservice can access another service's private database tables directly.
10. Academic Citation & Public Access
To cite this comparative empirical research in academic literature or industry reports, use:
Research Gate Publication
Access the official paper, citation metrics, and peer discussions on ResearchGate.
View DOI CitationCompanion Doctoral Study
Explore the complementary doctoral research (Doctorate in Business Administration with specialization in Data Science) on Cloud Transformation Challenges & Low-Code platforms.
Read LCNC Research