Monoliths

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

Topics
easymonolitharchitecturedeployment

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.

easymonolithtradeoffsscalability

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.

mediummodular-monolithboundariesmaintainability

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.

mediummodular-monolitharchitectureevolution

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.

hardmigrationstrangler-figmicroservices

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.

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.

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.

easymodular-monolithboundariesarchitecture

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.

mediummaintainabilitymodularityrefactoring

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.

mediumscalingstatelessload-balancer

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.

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”.

hardstrangler-figmigrationmicroservices

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.

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.

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.

harddistributed-monolithcouplingmicroservices

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.

easymonorepomonolithrepo-structure

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.

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”.

mediummodulesboundariesarchitecture-tests

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.

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.

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.

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.

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.

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.

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.

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.

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.

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.

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.

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.

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.

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.

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.

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.

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).

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.