Interview kitsBlog

Your dream job? Lets Git IT.
Interactive technical interview preparation platform designed for modern developers.

XGitHub

Platform

  • Categories

Resources

  • Blog
  • About the app
  • FAQ
  • Feedback

Legal

  • Privacy Policy
  • Terms of Service

© 2026 LetsGit.IT. All rights reserved.

Monoliths

Recruitment and knowledge question base. Filter, search and test your knowledge.

Topics

What is a monolithic architecture?

easymonolitharchitecturedeployment
Open question

Answer

A monolithic architecture is a single deployable application where modules run in one process and usually share one database. The system is built, tested and released as a unit, which is simple early on but can become tightly coupled as it grows.

Pros and cons of monoliths?

easymonolithtradeoffsscalability
Open question

Answer

Pros: simple development and deployment, easy local testing/debugging, and strong consistency with straightforward transactions. Cons: harder to scale parts independently, slower deploy cycles, and a growing codebase can become tightly coupled and harder to change safely.

How to keep a monolith maintainable as it grows?

mediummodular-monolithboundariesmaintainability
Open question

Answer

Keep clear module boundaries (often by domain), enforce dependency rules, keep layers thin, and add automated tests. Prefer internal APIs, limit shared state, and refactor regularly so the monolith stays modular and changeable.

What is a modular monolith?

mediummodular-monolitharchitectureevolution
Open question

Answer

A modular monolith is still one deployable unit, but with strong internal module separation. Each module owns its domain and communicates through well‑defined interfaces, improving maintainability and making future extraction to services easier.

Strategies to migrate from a monolith to microservices?

hardmigrationstrangler-figmicroservices
Open question

Answer

Prefer incremental migration (Strangler Fig): extract one feature/domain at a time behind stable APIs, and add good logging/metrics/tracing. Avoid big‑bang rewrites; split data and behavior gradually.

What is a monolith (and why is it not automatically “bad”)?

easymonolitharchitecturetradeoffs
Open question

Answer

A monolith is one deployable unit (one app/service) that contains multiple features/modules. It can be a great choice early because it’s simpler to develop, test, and deploy, and it keeps transactions and debugging straightforward.

When is a monolith a better choice than microservices?

easymonolithmicroservicesteam-size
Open question

Answer

When the team is small, the domain is still changing fast, and you want fast iteration with simple operations. Microservices pay off later when independent scaling/deployments and clear boundaries are truly needed.

How would you define a modular monolith?

easymodular-monolithboundariesarchitecture
Open question

Answer

It’s still one deployable unit, but with strong internal boundaries: modules own their domain and talk through well-defined interfaces. It improves maintainability and makes future extraction to services easier.

How do you keep a monolith maintainable as it grows?

mediummaintainabilitymodularityrefactoring
Open question

Answer

Define clear module boundaries (often by domain), enforce dependency rules, and keep layers thin. Add automated tests and refactor regularly so the codebase stays modular and easy to change.

How can you scale a monolith horizontally?

mediumscalingstatelessload-balancer
Open question

Answer

Make the app stateless (sessions in Redis/DB), put multiple instances behind a load balancer, and scale the database separately (indexes, caching, read replicas). Also watch shared resources like file storage and background jobs.

What are feature flags and why are they useful in a monolith?

mediumfeature-flagsrolloutrelease
Open question

Answer

Feature flags let you enable/disable features at runtime without redeploying. They support safer releases (dark launches, gradual rollout) and quick rollback, but require cleanup to avoid “flag debt”.

Strangler Fig migration — outline the steps.

hardstrangler-figmigrationmicroservices
Open question

Answer

Add a routing layer, pick one small domain/feature, extract it behind a stable API, and gradually shift traffic. Keep iterating one slice at a time, with good monitoring and rollback, until the old path can be removed.

Database split during extraction — what is the hardest part?

harddatabasemigrationdata-ownership
Open question

Answer

Data ownership and consistency: deciding which service owns which tables and how to keep data in sync during the transition (dual writes/outbox/events). The migration must be incremental with a clear cutover and rollback strategy.

How do you find good boundaries (seams) for extracting services from a monolith?

hardbounded-contextseamsdecomposition
Open question

Answer

Look for bounded contexts: features with clear ownership, data, and few dependencies. Start with parts that change often or have clear scaling needs, and avoid splitting tightly coupled areas first.

What is a “distributed monolith” and how do you avoid it?

harddistributed-monolithcouplingmicroservices
Open question

Answer

It’s a system split into services, but still tightly coupled (shared DB, synchronous chatty calls, coordinated deployments). Avoid it by clear ownership, async where appropriate, stable contracts, and independent deployability.

Monorepo vs monolith — what’s the difference?

easymonorepomonolithrepo-structure
Open question

Answer

Monorepo is a repository strategy (many projects in one repo). A monolith is a deployment/runtime unit (one app). You can have a monolith in a monorepo or microservices in a monorepo.

What is a “big ball of mud” and how can you recognize it?

mediummaintainabilitycouplingcode-smell
Open question

Answer

It’s a codebase with weak boundaries and lots of ad-hoc coupling. Signs: unclear ownership, changes causing random breakages, lots of global/shared state, and “everything depends on everything”.

How can you enforce module boundaries inside a monolith?

mediummodulesboundariesarchitecture-tests
Open question

Answer

Organize code by feature/domain, define clear public APIs between modules, and restrict dependencies (e.g., package rules, architecture tests). Keep shared utilities small and avoid “god” shared modules.

How do you introduce a breaking database change safely in a large monolith?

harddb-migrationexpand-contractdeployment+1
Open question

Answer

Use expand/contract: first add new schema (nullable column/new table) and deploy code that supports both; migrate data; then remove old schema in a later release. This avoids downtime and supports rollback.

What are two ways to scale a monolith without splitting into microservices?

hardscalingmonolithqueues+1
Open question

Answer

Scale horizontally (multiple stateless instances behind a load balancer) and move heavy work to async jobs/queues (background processing). You can also scale reads with caching and replicas.

Package-by-layer vs package-by-feature — what’s the difference?

easystructuremodularitymonolith
Open question

Answer

Package-by-layer groups code by technical layers (controllers/services/repos). Package-by-feature groups code by domain/feature. Feature-based structure often scales better because related code lives together and boundaries are clearer.

How do you run background jobs in a monolith reliably?

mediumjobsqueueworker+1
Open question

Answer

Use a separate worker process (same codebase, different entrypoint) consuming a queue, with retries and idempotency. This avoids blocking web requests and gives you better control over concurrency and failures.

How do you avoid a “utils” dumping ground in a growing monolith?

mediumshared-codeownershipmodularity
Open question

Answer

Keep utilities close to the feature that owns them, and extract shared code only when multiple modules truly need it. Prefer small, well-named shared libraries with clear ownership instead of one giant `utils` module.

How can you reduce CI/build time in a large monolith/monorepo?

hardcibuildcaching+1
Open question

Answer

Use caching (dependencies and build outputs), run tests/lints incrementally on changed modules, and parallelize jobs. Also keep modules independent so you don’t rebuild everything for a small change.

How do you refactor a messy monolith without stopping feature delivery?

hardrefactoringincrementaltesting+1
Open question

Answer

Work incrementally: pick one boundary, add tests around behavior, refactor behind stable interfaces, and ship in small steps. Use tech-debt budgeting and avoid big-bang rewrites.

What does “single deployable” mean and why is it a strength of a monolith?

easymonolithdeploymentrelease+1
Open question

Answer

Single deployable means you ship one artifact as one unit (one version to build, test, and deploy). It simplifies releases and rollbacks and avoids cross-service version mismatches. The trade-off is a bigger blast radius when something goes wrong.

How do you approach integration testing in a monolith without making CI too slow?

mediumtestingmonolithci+1
Open question

Answer

Follow a test pyramid: many fast unit tests, fewer integration tests, and a small number of end-to-end tests. For integration, test important seams (DB, messaging) with realistic dependencies (e.g., Testcontainers) and keep them parallelizable and stable. Avoid one giant “tests everything” suite.

How do you keep domain boundaries clear inside a monolith?

mediummonolithmodularityboundaries+1
Open question

Answer

Organize code by feature/domain (not only by technical layers), expose small internal APIs between modules, and forbid “reach-through” imports into other modules’ internals. Add ownership (who maintains what) and architectural checks (module boundaries) so boundaries don’t decay over time.

Multi-tenancy in a monolith: what are common data isolation approaches?

hardmonolithmulti-tenancysecurity+1
Open question

Answer

Common options: database-per-tenant (strong isolation, higher cost), schema-per-tenant (good isolation, moderate complexity), or shared tables with `tenant_id` (cheapest, hardest to enforce correctly). No matter what, you must enforce tenant scoping everywhere and add the right indexes and security checks.

How do you prevent performance regressions in a large monolith?

hardmonolithperformanceobservability+1
Open question

Answer

Define performance SLIs (e.g., p95 latency) and monitor them continuously. Add profiling/tracing for slow endpoints, use load tests for critical flows, and set budgets/alerts so regressions are caught early. Feature flags can help you roll back quickly if needed.

Structured logging: what is it and why is it useful in a monolith?

easymonolithsloggingobservability
Open question

Answer

Structured logging means logs are emitted as machine-readable fields (e.g., JSON) like `level`, `message`, `requestId`, `userId`. It’s useful because you can reliably search, filter, and correlate logs across many code paths without parsing random text.

Correlation ID in a monolith: what is it and where do you generate it?

mediummonolithsloggingrequest-id+1
Open question

Answer

A correlation ID (request ID) is a unique identifier added to a request and included in logs. Generate it at the edge (HTTP middleware/filter) or accept it from upstream, then pass it through all layers and background jobs triggered by the request.

How do you prevent cyclic dependencies between modules in a modular monolith?

mediummonolithsmodular-monolithboundaries+1
Open question

Answer

Define clear module boundaries and dependency direction rules (e.g., feature modules can depend on a shared kernel, but not on each other). Enforce it with the build system (separate Gradle/Maven modules), architecture tests, and by exposing only stable interfaces/facades instead of reaching into internals.

Domain events inside a monolith: why use them and what are the pitfalls?

hardmonolithsdddevents+1
Open question

Answer

They decouple modules: one module publishes an event (“OrderPlaced”) and others react without tight coupling. Pitfalls: deciding sync vs async handling, avoiding doing heavy work inside the same transaction, and ensuring handlers are reliable and idempotent (events can be retried or processed twice).

Shared database in a monolith: how do you avoid “shared-everything” between modules?

hardmonolithsboundariesdatabase+1
Open question

Answer

Treat tables as owned by modules: only the owner writes them and exposes access via its API/facade. Avoid cross-module joins in random places; instead, request data through the owning module or publish domain events. If needed, enforce it with separate schemas, repository boundaries, and code reviews/architecture rules.