Skip to content
Commitwise
Menu

Glossary

Every developer term explained in plain English, with real examples.

163 terms

@

Also: Typed configuration

Annotated with a prefix such as payment, the class receives every payment.* setting from application.yml, profiles, environment variables and other sources, converted to types like Duration, URI or enums. With @Validated, missing or invalid values stop the app at startup instead of failing later. It replaces scattered @Value strings and is easy to construct in tests.

In practice: PaymentProperties(baseUrl, timeout, retry) is injected into the payment client; PAYMENT_TIMEOUT=3s overrides the file.

Related:Spring bean

A

The interface has one creation method per product; each concrete factory (sandbox or live, brand A or brand B) creates products that work together. The app chooses one factory at startup and passes it in, and callers ask it for everything they need. Adding a family is one new class; adding a product type changes every factory. DI modules per build flavor or Spring profile are abstract factories in another form.

In practice: PaymentKit creates a client, a tokenizer and a webhook verifier; SandboxPaymentKit and LivePaymentKit each create a matching set.

Related:Factory MethodDesign pattern

Actor

Concurrency

An actor is like a class whose state can only be touched from inside the actor. Code outside must await to call it, and the actor handles those calls one at a time, so two tasks can never change its data at the same moment. The main actor is a special global actor for UI code. Actors are reentrant: while a method is suspended at an await, other calls can run, so state can change across an await.

In practice: actor ThumbnailCache { private var thumbnails: [URL: Data] = [:] } is safe to use from many parallel download tasks, each calling await cache.store(data, for: url).

Related:SendableData raceasync/await

Adapter

Design patterns

Also: Wrapper

An adapter implements your interface and translates every call to the wrapped class: names, parameters, units, formats, errors and callback styles. Placed at the edges of an app, adapters keep vendor SDKs and external APIs from leaking into the rest of the code, so replacing a vendor touches one class.

In practice: A PayFastAdapter implements PaymentGateway.charge(cents:) by converting cents to a decimal amount and calling the PayFast SDK.

Related:Decorator patternDesign pattern

Amortized time

Algorithms

Some operations are usually fast but occasionally slow, like appending to a dynamic array that sometimes has to grow and copy everything. Amortized analysis spreads the rare expensive work over all the cheap operations. Because arrays double their capacity, n appends cost O(n) in total, so each one is O(1) amortized, even though a single append can be O(n).

In practice: Appending to a Swift Array, a Kotlin MutableList or a Java ArrayList is O(1) amortized.

Related:Big-O notation

async/await

Concurrency

With async/await, a function marked async (Swift) or suspend (Kotlin) can wait for slow work without blocking a thread. Each await is a point where the function may pause and resume later. It replaces nested callbacks with straight-line code and makes error handling work with normal try/catch.

In practice: let user = try await api.user(id: 42) in Swift waits for the request, then continues on the next line.

Related:Coroutine

Spring Boot ships configuration classes guarded by conditions such as "this class is on the classpath", "no bean of this type exists yet" or "this property is set". When the conditions match, Boot creates beans like a JSON mapper, a data source or a web server with sensible defaults. Defining your own bean of the same type makes the auto-configured one step aside, together with the properties it applied.

In practice: Adding spring-boot-starter-web gives you an embedded Tomcat and a configured Jackson mapper without writing any configuration.

Related:Spring beanInversion of control

Also: ARC

Automatic Reference Counting (ARC) adds and removes reference counts at compile time. When the last strong reference to an object goes away, it is freed immediately, which makes memory use predictable. The catch is retain cycles: two objects that strongly reference each other never reach zero, so you break the cycle with weak or unowned.

In practice: A view model holds a closure that captures self strongly, and the closure is stored on the view model. Neither is ever freed until you write [weak self] in the closure.

Related:Garbage collectionRetain cycle

Availability

System design

Also: Uptime, Nines

Availability is usually measured as successful requests or uptime over a period. Each extra nine cuts allowed downtime by ten. Services that depend on each other in series multiply their availability, so a chain is weaker than its weakest link, while redundant copies multiply their unavailability and make a component much more reliable.

In practice: Three dependencies at 99.9% each, called one after another, give at most about 99.7%.

Related:Load balancer

B

Back stack

Android

Navigation libraries keep one entry per visited destination, each with its own lifecycle, ViewModels and saved state. Options like popUpTo, launchSingleTop and saveState shape the stack so Back does what users expect: login and onboarding disappear after use, and bottom navigation tabs keep their own history.

In practice: After login, navigate(Home) { popUpTo<Login> { inclusive = true } } leaves only Home, so Back closes the app.

Related:StackDeep link

Also: Capacity estimate

Instead of exact figures, you use rough facts such as "a day has about 100,000 seconds" to turn daily numbers into per-second rates, add a factor for peaks, and multiply sizes to get storage. The goal is the order of magnitude and what it implies: whether data fits in memory, whether one database is enough, whether files belong in object storage.

In practice: 100 million feed loads a day is about 1,000 per second on average, and maybe 3,000 at peak.

Related:Non-functional requirements

Also: BGTaskScheduler

beginBackgroundTask buys a short window to finish a job; a background URLSession continues uploads and downloads even after the app is terminated; BGTaskScheduler runs app refresh and processing tasks when the system decides, based on usage, battery and network. Handlers must reschedule, handle expiration and complete promptly.

In practice: A news app schedules a BGAppRefreshTask so headlines are fresh shortly before the user usually opens it.

Related:Scene phase

Backtracking

Algorithms

Backtracking is a depth-first search over choices: choose an option, recurse to make the next choice, then undo the option and try the next one. It lists all subsets, permutations or combinations, and solves constraint puzzles like Sudoku. Pruning, which stops exploring a branch as soon as it can't succeed, is what keeps it fast enough in practice.

In practice: To list all subsets of [1, 2, 3], decide for each number whether to include it, which gives 2 x 2 x 2 = 8 subsets.

Related:Dynamic programmingRecursion

Base62

System design

Base62 is like base 10, but every position has 62 possible characters instead of 10. That packs a large number into few characters without symbols that need escaping in URLs. URL shorteners use it to turn a numeric ID into a short code: 7 characters can represent about 3.5 trillion different values.

In practice: The number 125 is "21" in base62, because 125 = 2 × 62 + 1.

Related:Sharding

Without it, much of an app's code runs in the interpreter or JIT after install until the system compiles it in the background, which can take days. Baseline profiles are generated by running startup and critical user journeys with Macrobenchmark's BaselineProfileRule, and applied by Play at install time or by ProfileInstaller for sideloaded builds. Google reports typical first-launch improvements of around 30%.

In practice: The profile covers app startup and the first scroll of the product list, so both are fast right after an update.

Related:JankR8

Big-O notation

Algorithms

Also: Time complexity, Space complexity

Big-O describes the growth rate of an algorithm, ignoring constants and small inputs. O(1) stays the same no matter the input size, O(n) grows in step with it, O(n²) grows with its square. Interviewers expect you to state both the time and the space complexity of your solution.

In practice: Checking a list for duplicates with two nested loops is O(n²); putting items in a hash set as you go is O(n).

Related:Hash map

Also: BST

The ordering rule lets you search, insert and delete by walking down one path, which takes O(log n) when the tree is balanced and O(n) when it degenerates into a line. An in-order traversal visits the values in sorted order. Sorted maps like Java's TreeMap use self-balancing variants such as red-black trees.

In practice: To find 7 in a BST rooted at 4, go right (7 > 4), then right again if the next node is 6.

Related:Binary search

Builder pattern

Design patterns

Also: Builder

A builder collects the settings for an object through named methods and creates the finished object in one final build() call. It replaces long constructors with many optional parameters, keeps the final object immutable, and gives one place to check that the combination of settings is valid. In Kotlin and Swift, named and default arguments often make a builder unnecessary.

In practice: OkHttpClient.Builder().connectTimeout(10, SECONDS).addInterceptor(auth).build() configures only what differs from the defaults.

Related:Design patternFluent interface

Bulkhead

System design

Named after the watertight compartments in a ship. A bulkhead caps how many requests to, say, a recommendations service can be in progress at once; extra calls are rejected immediately or wait briefly, and the caller falls back. Rate limits count requests per second, but slow requests pile up even at a constant rate, which is what bulkheads and other concurrency limits protect against. Every permit must be released, including on errors and timeouts.

In practice: At most 50 concurrent calls go to recommendations; when they're all busy, product pages show no recommendations instead of timing out.

Related:Load sheddingToken bucket

C

Cache stampede

System design

Also: Thundering herd, Dog-pile effect

When a popular key expires or the cache is flushed, every concurrent request misses and queries the database, often overloading it exactly when traffic is highest. Defenses are letting only one request rebuild each key (single flight or a lock), refreshing hot keys before they expire, and adding random jitter to TTLs so keys don't expire together.

In practice: A home page cached for 60 seconds expires at 12:00:00 and 5,000 requests query the database in the same second.

Related:Cache-asideCaching

Cache-aside

System design

Also: Lazy loading

Also called lazy loading. Reads try the cache, fall back to the database, and fill the cache with a TTL. Writes update the database and delete the key, so the next read loads fresh data. The TTL bounds how long data can be stale if an invalidation is missed. It's the most common caching strategy because a cache failure only makes reads slower.

In practice: product(id) reads product:id from Redis; on a miss it queries PostgreSQL and stores the result for 10 minutes.

Related:CachingCache stampede

Caching

System design

Also: Cache

A cache stores results close to where they are needed: in memory, on disk, in Redis or on a CDN. It makes reads faster and reduces load on the source, at the cost of data that may be stale. Every cache needs a plan for invalidation (when to throw old data away) and a size limit.

In practice: An image loader keeps recently shown thumbnails in an in-memory LRU cache, so scrolling back up doesn't re-download them.

Related:Load balancer

CAP theorem

System design

Also: PACELC

C is linearizability (every read sees the latest write), A is that every working server answers, and P is tolerating network partitions. Partitions happen anyway, so the real choice is C or A during a partition, made per operation: uniqueness, money and inventory usually choose consistency; carts, likes and feeds choose availability. PACELC adds that, without partitions, consistency still costs latency.

In practice: During a link failure, a consensus database's minority side refuses writes while the majority side keeps working.

Related:Replication lagCRDT

Also: Edge cache, PoP

Requests go to the nearest edge server; a cache hit answers in milliseconds, a miss is fetched from the origin (often through an origin shield) and stored. Cache-Control headers decide what may be cached and for how long; personal responses must be private. Files are versioned with content hashes in their URLs instead of purged, and private files are served with short-lived signed URLs.

In practice: /assets/app.8d2f1c.js is cached for a year at every edge; a new release references app.9e44a0.js instead.

Related:Cache-aside

Also: Middleware, Interceptor chain

In the routing form, each handler handles the request or passes it on; specific handlers come before general ones and a fallback ends the chain. In the middleware form (servlet filters, OkHttp interceptors, Express middleware), every handler can act before and after calling next, must call it exactly once unless it deliberately stops or retries, and the order of handlers decides what each one sees.

In practice: Deep links go through order, sale, product and promo handlers, and fall back to the home screen.

Related:Deep linkInterceptor (OkHttp)

In Java, exceptions that extend Exception but not RuntimeException are checked: a method that can throw one must say so with throws, and every caller must catch it or declare it too. They make failures visible in signatures, but they don't work well with lambdas and can leak implementation details. Kotlin has no checked exceptions, and Swift marks failure with throws and try instead.

In practice: Files.readString(path) throws IOException, so code calling it must handle or declare IOException.

Related:Result type

Clean Architecture

Architecture

The idea, popularized by Robert C. Martin, is that the core logic of an app should be plain code that can be tested and reused without the UI or the storage technology. On Android it usually appears in a pragmatic form: a UI layer, an optional domain layer with use cases, and a data layer with repositories. Taken too far, it produces pass-through use cases and a mapper for every layer without isolating any real logic.

In practice: CanCancelOrderUseCase is plain Kotlin used by two screens and tested with a fake clock; it knows nothing about Compose or Room.

Related:Use caseDependency inversion principleMVVM

Command pattern

Design patterns

Also: Undo stack

A command holds everything needed to perform one action, and often to reverse it. Undo and redo stacks store executed commands; offline queues store commands as data and send them when the network returns. Persisted commands are delivered at least once, so each carries an id the server uses to ignore duplicates.

In practice: MoveShape(shapeId, dx, dy) moves a shape in execute() and moves it back in undo().

Related:Idempotency

Composite pattern

Design patterns

A leaf answers from its own data; a composite holds children of the shared interface type and answers by asking them and combining the results, recursively. New item types implement the interface instead of being added to type checks scattered through the code. Trees loaded from data need protection against cycles and very deep nesting.

In practice: A Bundle's price is the sum of its children's prices minus its discount, whether the children are products or other bundles.

Related:RecursionDesign pattern

Composition root

Architecture

Everything else receives its dependencies instead of creating them, so swapping implementations (fakes for tests, previews or UI tests, different environments) means changing only the composition root. In a SwiftUI app it is usually the App struct; in an Android app with Hilt, the DI modules play this role.

In practice: RecipesApp creates AppDependencies.live(), or fakes when launched with --ui-testing, and passes them to the first screen.

Related:Dependency injection

Android recreates the Activity so layouts, strings and colors for the new configuration are used. Fields and remember values are lost; the ViewModel survives; small UI state survives in rememberSaveable or saved instance state. Work started in the Activity is cancelled or orphaned, which is why loading belongs in the ViewModel.

In practice: Rotating the phone during a profile load starts a second request unless the request lives in the ViewModel.

Related:Process deathUnidirectional data flow

Lag is measured per partition and summed per group. Steady low lag is healthy; lag that keeps growing means consumers can't keep up or are stuck on a message. Lag, and the age of the oldest unprocessed message, are the main signals for alerting on and autoscaling message consumers.

In practice: After a deploy, the email group's lag climbs to 400,000 because a deserialization error stopped one partition.

Related:Partition (Kafka)Dead-letter queue

Also: Docker image, OCI image

An image is built from a Dockerfile (or by Buildpacks) as a stack of layers, each the result of one build step. Layers are cached and shared, so putting rarely changing parts (the base image, dependencies) first and the application code last means a release only ships a few megabytes. Images are tagged and pushed to a registry, and should never contain secrets.

In practice: registry.example.com/checkout:3f9a2c1 is the checkout service built from commit 3f9a2c1.

Related:Testcontainers

Coordinator pattern

Design patterns

Also: Router

Screens expose callbacks such as onOrderSelected or onSaved; the coordinator creates screens, wires those callbacks and decides what comes next. Coordinators form a tree: an app coordinator starts child flows such as login or checkout, which report a result when they finish and are then released. With state-driven navigation (SwiftUI NavigationStack(path:), Compose navigation graphs) the coordinator becomes a router that owns the navigation state. Deep links become coordinator methods that build the right stack.

In practice: The address form calls onSaved(address); the checkout coordinator pops back to the summary, the settings coordinator shows a banner.

Related:Deep linkDesign pattern

Also: CoW

Copy-on-write (CoW) lets value types hold large data cheaply. Assigning the value copies only a reference to a shared buffer; the first mutation checks whether the buffer is shared and, if it is, copies it before writing. Swift's Array, Dictionary, Set and String work this way. Extra references to the same buffer make every mutation copy, which can turn a loop of appends into quadratic work.

In practice: var copy = items followed by items.append(x) copies the whole array once, because the buffer is shared with copy at the moment of the append.

Related:Value type

Coroutine

Concurrency

A coroutine can suspend at a suspend call, for example while waiting for the network, and free its thread for other work. When the result arrives, it resumes where it left off. Thousands of coroutines can share a handful of threads, and structured concurrency (scopes) makes sure they are cancelled together.

In practice: viewModelScope.launch { val user = api.loadUser() } loads a user without freezing the UI, and is cancelled automatically when the screen's ViewModel is cleared.

Related:async/await

CRDT

System design

Also: Conflict-free replicated data type

CRDTs include counters (each replica increments its own slot; the value is the sum), sets that handle removals (observed-remove sets), registers and sequences for collaborative text. They let offline devices and multiple regions accept writes without coordination and converge without losing updates, unlike last-write-wins.

In practice: A like counter in two regions: each counts its own likes, and the total is the sum, so no concurrent like is lost.

Related:CAP theorem

Cursor pagination

System design

Also: Keyset pagination

The server returns an opaque cursor with each page, usually encoding the sort key and a tie-breaking id of the last item. The next request asks for items after that cursor. Unlike offsets, cursors don't produce duplicates or skipped items when new items are inserted or old ones deleted while the user is scrolling.

In practice: GET /feed?cursor=eyJ0IjoxNzMx... returns the next 20 posts older than the last one the app received.

Related:Fan-out

D

Data race

Concurrency

Also: Race condition

Data races corrupt memory and cause crashes or wrong results that are rare, unpredictable and hard to reproduce, because they depend on exact timing. Locks, actors, immutable data and confining state to one thread all prevent them. Swift 6 rejects code that could race at compile time; on the JVM, tools and careful design (immutable objects, concurrent collections) are the main defense.

In practice: Two tasks writing to the same Swift Dictionary inside a plain class can crash inside the dictionary's code once in thousands of runs.

Related:ActorSendable

Database index

System design

Also: Index

Most indexes are B-trees: lookups and range scans take a logarithmic number of steps instead of a full scan. A composite index on (a, b) helps queries that filter on a, or on a and b, but not on b alone. Every index makes writes slower and uses space, so indexes are added for the queries an app actually runs, and checked with EXPLAIN.

In practice: An index on orders (customer_id, created_at) turns "a customer's last 10 orders" from a 20-million-row scan into a 10-row read.

Related:ShardingBig-O notation

Also: Schema migration

Installed apps keep their database across updates, so every schema change (a new column, a renamed table) needs a migration from the old version to the new one. Room supports auto-migrations for simple changes and hand-written SQL migrations for the rest; backends use tools like Flyway. Migrations are tested from old versions, because users skip updates.

In practice: Version 3 of a notes app adds a color column; the migration runs ALTER TABLE notes ADD COLUMN color INTEGER NOT NULL DEFAULT 0.

Related:Transactional outbox

Dead-letter queue

System design

Also: DLQ

After a message fails a set number of times (malformed data, a bug, a permanently missing record), the consumer or broker moves it to a dead-letter queue instead of retrying forever. Someone must be alerted and fix or replay those messages; a dead-letter queue nobody watches silently loses work.

In practice: An OrderPlaced event with an invalid currency goes to orders-dlq after 5 attempts, and an alert fires.

Related:Message queueIdempotency

Declarative UI

Architecture

In imperative UI code you find a label and change its text. In declarative UI you write a function or view that returns the whole screen for a given state, and the framework compares it with what is shown and applies the differences. SwiftUI, Jetpack Compose, React and Flutter all work this way, which removes a whole class of bugs where the screen and the data disagree.

In practice: A like button is described as a filled heart when isLiked is true and an empty one otherwise; tapping only flips isLiked.

Related:RecompositionUnidirectional data flow

Decorator pattern

Design patterns

Also: Decorator, Wrapper

A decorator implements the same interface as the object it wraps, does its extra work (logging, caching, retrying, measuring) and delegates the real work to the wrapped object. Decorators combine freely, so each behavior is one small class instead of a subclass per combination. The order of wrapping changes behavior, and decorators apply to every method of the interface.

In practice: new BufferedReader(new InputStreamReader(stream)) adds decoding and buffering around a plain stream.

Related:Design pattern

Also: DI

With dependency injection (DI), a class receives its collaborators, such as an API client or a database, through its constructor or properties. That makes it easy to swap a real implementation for a fake in tests, and to change implementations without touching the class. Frameworks like Hilt and Spring automate the wiring, but plain constructor parameters are DI too.

In practice: OrderService(repository: OrderRepository) can be tested with an in-memory repository instead of a real database.

Related:MVVMDesign pattern

Also: DIP

High-level code should not depend on low-level details; both should depend on an abstraction owned by the high-level side. At runtime checkout still calls the payment provider, but in the source code the provider's adapter depends on checkout's PaymentGateway interface, not the other way round. Dependency injection is a common way to supply the implementations.

In practice: CheckoutService asks for a PaymentGateway; a StripeGateway class in the infrastructure layer implements it.

Related:Dependency injectionInversion of control

Design pattern

Design patterns

Also: Pattern

A design pattern is not code you copy but a proven shape for solving a recurring problem, such as creating objects flexibly (Factory, Builder), adapting interfaces (Adapter) or reacting to changes (Observer). The names give teams a shared vocabulary: "make it a Strategy" says a lot in three words.

In practice: Payment methods (card, PayPal, Apple Pay) implement one PaymentStrategy interface, so checkout code doesn't need an if for each one.

Related:Dependency injection

Also: Tracing, Span, Trace id

The first service creates a trace id and passes it in the traceparent header (W3C Trace Context) to every downstream call and message. Each service records spans (name, start, duration, attributes, parent) and exports them, for example via OpenTelemetry. The resulting tree shows where the time went, and putting the trace id in every log line connects logs to traces.

In practice: A slow checkout trace shows 1.8 s of its 2 s spent in a single call to the payment provider.

Related:Metric cardinality

Downsampling

System design

A decoded bitmap uses width × height × 4 bytes regardless of the file size, so a 12-megapixel photo takes about 48 MB in memory. Decoding directly at the displayed size (CGImageSourceCreateThumbnailAtIndex on iOS, ImageDecoder.setTargetSize or inSampleSize on Android) never allocates the full bitmap. Image libraries such as Coil, Glide, Kingfisher and Nuke downsample to the view's size automatically.

In practice: A 4000 × 3000 photo shown in a 120-point thumbnail on a 3× screen is decoded at 360 × 360, about 0.5 MB.

Related:Lazy loading

DTO

Backend

Also: Data transfer object

A DTO carries data between layers or over the network, with no business logic. Using separate request and response DTOs instead of database entities keeps the API contract stable when the database changes, exposes only the fields the client needs, and gives validation annotations a natural home.

In practice: CreateBookRequest(title, author, year) is what the app sends; BookResponse(id, title, author, year) is what it gets back.

Related:REST API

Also: DP

Dynamic programming applies when a problem breaks into smaller versions of itself that repeat. You define a state (what a subproblem is), a transition (how a state follows from smaller ones, usually by asking what the last decision was), base cases and an order, then compute each state once, either top-down with memoization or bottom-up by filling a table.

In practice: The fewest coins for an amount equals one coin plus the fewest coins for the amount minus that coin, minimized over all coins.

Related:MemoizationBig-O notation

Users choose from extra small to accessibility sizes several times larger than the default. Text styles such as .body and .headline follow the setting; fixed point sizes don't. Layouts must let rows grow, avoid truncating essential text and switch from side-by-side to stacked arrangements at the largest sizes, and @ScaledMetric scales icons and spacing along with the text.

In practice: At the largest size, an order row stacks its icon, title and status vertically instead of squeezing them into one line.

Related:VoiceOver

E

Extension

Language

Also: Extension function, Extension method

Swift extensions and Kotlin extension functions let you call new helpers with normal dot syntax, such as name.initials. They can't store new data or override existing methods, and they are chosen by the variable's declared type at compile time. Java has no extensions and uses static helper methods instead; TypeScript uses plain functions.

In practice: extension Date { var isWeekend: Bool { … } } lets any Swift code write date.isWeekend.

Related:Type inference

F

Facade pattern

Design patterns

A facade coordinates a subsystem (several services that must be called in the right order) behind methods named after what callers want, such as placeOrder(cart). It owns the sequence of steps and what happens when one fails, but holds no business rules of its own. The subsystem stays available for callers that need more control. A facade that collects every method of every subsystem turns into a god object.

In practice: CheckoutFacade.placeOrder reserves stock, charges the card, creates the order and releases the stock if payment fails.

Related:AdapterDecorator patternProxy pattern

Factory Method

Design patterns

Also: Factory, Static factory method

In the classic form, a base class's routine calls an overridable method to create the object it works with, and each subclass returns a different kind. More broadly, factories and static factory methods (List.of, URL(string:)) keep construction in one place, can validate input, return cached immutable instances or subtypes, and let callers depend only on an interface.

In practice: factory.sender(Channel.SMS) returns a NotificationSender; only the factory knows it's an SmsSender with an API key.

Related:Design patternDependency injection

Fan-out

System design

In feed design, fan-out on write pushes each new post id into every follower's cached feed, so reading a feed is one lookup but posting is expensive for accounts with many followers. Fan-out on read gathers posts from all followed accounts when the feed is opened, which is cheap to write but slow to read. Large systems combine them: push for normal accounts, pull for celebrities.

In practice: A post by an account with 200 followers becomes 200 inserts into Redis feed lists.

Related:Cursor pagination

Fluent interface

Design patterns

In a fluent interface each call returns the same builder (or a new immutable copy), so calls chain: request.url(u).header(k, v).timeout(t). Builders, query DSLs and stream pipelines are common examples. Chains are easy to read, but a mutable builder shared between threads or reused after build() can leak settings between objects.

In practice: Java's HttpRequest.newBuilder(uri).header("Accept", "application/json").GET().build().

Related:Builder pattern

G

Also: GC

The JVM and Android runtime use a garbage collector (GC). Instead of counting references, it starts from live roots (threads, static fields) and frees everything it can't reach, so reference cycles are not a problem. The cost is that collection happens at times you don't control, which can cause short pauses.

In practice: A Spring service creates thousands of short-lived DTOs per request; the GC cleans them up in the young generation without you writing any cleanup code.

Related:Automatic Reference Counting

Generics

Language

Generics let you write code with a placeholder type, like Array<Element> or List<T>, that is filled in when the code is used. You get one implementation that works for strings, users or orders, and the compiler still stops you from mixing them up.

In practice: A Cache<Key, Value> class can store images by URL in one place and users by ID in another, with no casts.

Related:Type inference

Also: Greedy

Greedy algorithms are simple and fast, often a sort plus one pass, but they are only correct when a locally best choice can never ruin the overall result. That has to be argued, usually with an exchange argument: any optimal solution can be changed step by step into the greedy one without getting worse. When the argument fails, dynamic programming is the usual alternative.

In practice: To fit the most meetings into one room, always pick the meeting that ends earliest among those that still fit.

Related:Dynamic programming

H

Hang

iOS

Common causes are synchronous disk or network access, heavy computation or image decoding on the main thread, and lock contention. On iOS, the Hangs instrument, Xcode's Organizer and MetricKit report them; on Android, the equivalent is jank for short stalls and an ANR when the main thread is blocked for about five seconds. Async code running on the main actor still blocks it between suspension points.

In practice: Rendering a PDF synchronously in a @MainActor method freezes the share button for a second.

Related:Jank

Hash collision

Algorithms

Also: Collision

A hash map has far fewer buckets than possible keys, so different keys sometimes map to the same bucket. The map handles this by chaining entries in the bucket or probing for the next free slot, and uses equals (==) to find the right one. A few collisions are normal; many collisions make lookups slow.

In practice: In Java, "Aa" and "BB" have the same hashCode (2112), so they always land in the same bucket.

Related:Hash mapLoad factor

Hash map

Algorithms

Also: Dictionary, Hash table

A hash map turns each key into a number (its hash) and uses it to jump straight to the bucket where the value is stored. Lookups, inserts and deletes are O(1) on average. Swift calls it Dictionary, Kotlin and Java HashMap (Kotlin's mutableMapOf() keeps insertion order with a LinkedHashMap).

In practice: Counting word frequencies: counts[word, default: 0] += 1 in Swift touches each word once, O(n) in total.

Related:Big-O notation

Health check

System design

Also: Readiness probe, Liveness probe

A readiness check answers "should I get traffic?" and fails while a server is starting, overloaded or shutting down; a liveness check answers "am I stuck and need a restart?". A load balancer stops routing to a server after a few failed checks, so detection takes interval times threshold seconds.

In practice: GET /health/ready returns 503 during shutdown, so the load balancer stops sending new requests before the process exits.

Related:Load balancer

Heap dump

Memory

Taken with jcmd <pid> GC.heap_dump or automatically on OutOfMemoryError, and opened in Eclipse MAT or VisualVM. The dominator tree shows which objects retain the most memory, and the path to GC roots shows why they are still reachable. Dumps can contain personal data and secrets, so treat them like production data.

In practice: A dump shows one ConcurrentHashMap in an exchange-rate service retaining 1.2 GB through 8 million entries.

Related:Garbage collection

Horizontal scaling

System design

Also: Scaling out

Scaling out (horizontally) adds servers behind a load balancer; scaling up (vertically) replaces a server with a bigger one. Horizontal scaling has no practical upper limit and survives single-server failures, but only works if servers are stateless: sessions, carts and files must live in shared stores, so any server can handle any request.

In practice: Going from 2 to 10 identical API servers during a sale, with carts stored in Redis instead of server memory.

Related:Load balancerHealth check

Also: Status code

2xx codes mean success (200 OK, 201 Created, 204 No Content), 3xx mean redirection (301, 302), 4xx mean the client made a mistake (400 Bad Request, 401 Unauthorized, 404 Not Found, 409 Conflict) and 5xx mean the server failed (500, 503). Apps decide what to show the user based on them, so returning the right one matters.

In practice: A login with a wrong password should return 401, so the app shows "Wrong password" instead of "Something went wrong".

Related:REST API

I

Also: Idempotent

Networks fail, so clients retry. Retrying an idempotent request is safe: GET, PUT and DELETE are idempotent by definition. POST isn't, so a retried POST can create a duplicate. APIs that handle money usually accept an idempotency key with each POST and return the stored result when the same key arrives again.

In practice: The app times out while paying and retries. With an idempotency key, the customer is charged once, not twice.

Related:REST API

Also: BOLA, Broken object level authorization

Authentication proves who the caller is; it doesn't prove they may access order 1042. IDOR (called BOLA, Broken Object Level Authorization, in the OWASP API list) happens when an endpoint loads an object by the id from the URL and never checks ownership. The fix is to scope every query to the caller, for example findByIdAndCustomerId(id, userId), and to return 404 rather than 403 for objects that aren't theirs.

In practice: Changing /api/orders/1042 to /api/orders/1043 shows another customer's order and address.

Related:JWT (JSON Web Token)

An interceptor receives the outgoing request, can change it (for example adding an Authorization header), passes it on with chain.proceed(), and can inspect or change the response. Application interceptors run once per call; network interceptors see every network attempt, including redirects. Token refresh on 401 belongs in an Authenticator, which OkHttp calls when the server rejects the credentials.

In practice: An AuthInterceptor adds "Bearer <token>" to every request, so no Retrofit function has to mention the token.

Related:REST API

Also: ISP

When an interface bundles many abilities, classes that can only provide some of them have to stub or throw in the rest, and clients that use one method still depend on all of them. Splitting it into role interfaces, which one class may implement several of, keeps every promise keepable.

In practice: DocumentStorage becomes DocumentReader and DocumentWriter, so a read-only backup implements only the reader.

Related:Liskov substitution principle

Also: IoC

With inversion of control, your classes declare what they need and a container (Spring's ApplicationContext, Hilt, a composition root you write yourself) builds the object graph and decides when your code runs. Dependency injection is the most common form. It keeps classes focused on their job and makes them easy to test with fakes.

In practice: You never call new OrderService(...) in a Spring app; Spring creates it and passes it to the controller that needs it.

Related:Dependency injectionSpring beanComposition root

Iterator pattern

Design patterns

The collection creates a fresh iterator for each traversal, and the iterator keeps the position. Swift Sequence, Kotlin and Java Iterable and TypeScript's Symbol.iterator are built-in forms, so implementing them makes for loops work. Lazy and async iterators (generators, Flow, AsyncSequence) produce elements only when asked, which suits paged APIs. A collection must not be modified while it is being iterated.

In practice: for query in recentSearches walks a ring buffer newest first without exposing its array or index.

Related:Lazy loading

J

Jank

Android

At 60 Hz the app has about 16 ms to produce each frame, at 120 Hz about 8 ms. Work on the main thread, heavy recomposition or layout, and garbage collection pauses can push a frame past that budget, so it is shown late or skipped. Jank is measured on release builds on real devices, with tools such as Macrobenchmark, Perfetto and JankStats.

In practice: Formatting prices inside a composable on every scroll frame makes the product list stutter on mid-range phones.

Related:RecompositionBaseline profile

JIT compiler

Language

Also: Just-in-time compilation, HotSpot

The JVM starts by interpreting bytecode and profiles which methods are hot. Those are compiled, first quickly (C1) and then with aggressive optimizations based on observed behavior (C2): inlining, removing unused code and checks that never fail. This is why Java services get faster during their first minutes, and why naive benchmarks mislead.

In practice: The first 500 requests after a deploy take 80 ms on average, later ones 15 ms, once the hot paths are compiled.

Related:Garbage collection

Also: Bearer token, Access token

A JWT has three Base64URL parts: a header, a payload of claims (sub, scope, exp, iss, aud) and a signature. The server verifies the signature with the issuer's key and checks expiry, issuer and audience. The payload is readable by anyone, so it must not hold secrets, and a token stays valid until it expires, which is why access tokens are short-lived and paired with refresh tokens.

In practice: A Spring Boot resource server validates the Bearer token on every request and reads the user id from jwt.getSubject().

Related:IDOR (Insecure Direct Object Reference)

L

In JPA and Hibernate, a lazy association (author.books, or a @ManyToOne marked LAZY) is a placeholder until code touches it; then Hibernate runs a query to fill it. This avoids loading data nobody needs, but it needs an open session: touching a lazy association after the transaction ends throws LazyInitializationException, and touching one per item in a loop causes the N+1 problem.

In practice: Loading 20 orders and then reading order.getLines() for each runs 1 + 20 queries unless the lines are fetched with a join or in a batch.

Related:N+1 query problem

Also: LSP

A subtype has to keep the whole contract of its parent: accept at least the same inputs, promise at least the same results, and keep the same rules, including unwritten ones like not throwing for valid input. A subclass that throws "not supported" or changes behavior callers rely on breaks code that was written for the parent.

In practice: A ReadOnlyBackup that implements DocumentStorage but throws in save breaks every caller that archives documents.

Related:Interface segregation principle

Load balancer

System design

Also: LB, Reverse proxy

A load balancer sits in front of a group of servers and forwards each request to one of them, for example round-robin or to the least busy one. It lets you scale horizontally by adding servers, and takes unhealthy servers out of rotation using health checks.

In practice: During a sale, an online shop adds ten more API instances behind its load balancer without changing the app.

Related:Caching

Load factor

Algorithms

When the number of entries divided by the number of buckets passes the load factor, the map allocates a bigger bucket array and moves every entry. Java's HashMap uses 0.75 by default. A lower load factor means fewer collisions but more memory. Because resizing doubles the size, inserts stay O(1) on average.

In practice: A Java HashMap with 16 buckets resizes to 32 when the 13th entry is added (16 × 0.75 = 12).

Related:Hash mapHash collision

Load shedding

System design

Instead of queuing everything and timing out late, an overloaded service rejects work early with a cheap response (usually 503 with Retry-After), starting with low-priority requests such as prefetching or analytics. Signals for shedding include CPU, queue length and how long a request has already waited. Work for clients that have already given up is pure waste.

In practice: During a traffic spike, the API rejects background sync requests so checkout requests keep succeeding.

Related:Bulkhead

M

Memoization

Algorithms

Memoization stores the answer for each input the first time a function computes it and returns the stored answer on later calls. In dynamic programming it turns exponential recursions into fast ones, because the same subproblems are reached many times. The cache must be shared by all calls of one computation; a cache created inside the function on each call remembers nothing.

In practice: A memoized Fibonacci computes fib(50) with about 100 calls instead of over 40 billion.

Related:Dynamic programmingCaching

Also: Leak

In languages with ARC or a garbage collector, leaks aren't forgotten free() calls; they are references nobody remembered: a retain cycle under ARC, or an object still reachable from something long-lived, such as a singleton's listener list, a static cache or a running timer. Leaked screens keep their whole view tree in memory, so memory grows every time the user opens them, until the system kills the app.

In practice: A screen registers a listener with an app-wide download manager and never removes it, so every closed screen stays in memory.

Related:Retain cycleGarbage collectionWeak reference

Message queue

System design

Also: Queue, Message broker

A queue decouples the producer of work from the consumer: the request that uploads a photo returns immediately, and workers create thumbnails and update feeds when they get to it. Queues absorb traffic spikes, survive a slow or failed consumer, and let each side scale separately. Kafka, RabbitMQ, SQS and Redis streams are common choices.

In practice: A checkout service publishes "order placed", and separate workers send the email and update the warehouse.

Related:QueueTransactional outbox

A metric with tags method (5 values), status (10) and uri (50 templates) can produce 2,500 series, which is fine. Adding a customerId tag multiplies that by the number of customers, and the metrics backend runs out of memory or the bill explodes. Tags must have a small, bounded set of values; per-entity detail belongs in logs and traces.

In practice: http.server.requests tagged with the URI template /orders/{id}, not the raw path /orders/1042.

Related:Distributed tracing

Multipart upload

System design

Also: Resumable upload, Chunked upload

The client starts an upload, sends numbered parts (each with its own presigned URL, possibly in parallel), can ask which parts storage already has to resume after a failure, and finally completes the upload so the parts are assembled into one object. Incomplete uploads keep their parts and are billed until they are aborted, so buckets need a lifecycle rule to clean them up.

In practice: A 400 MB video is sent as 50 parts of 8 MB; after a tunnel, the app resumes from part 37.

Related:Presigned URL

MVI

Architecture

Also: Model-View-Intent, Redux-style architecture

In MVI, every user action or result becomes an intent, a pure reducer turns (state, intent) into the next state, and side effects like network calls run outside the reducer and send their results back as intents. Data flows in one direction, which makes changes easy to trace and test. One-off events such as toasts and navigation need special handling so they are not replayed.

In practice: reduce(state, LogInTapped) returns the same state with isLoading = true; the store then starts the login call.

Related:MVVMUnidirectional data flowState machine

MVVM

Architecture

Also: Model-View-ViewModel

In MVVM, the Model is your data and business logic, the View draws the screen, and the ViewModel turns model data into screen state and handles user actions. The View observes the ViewModel and redraws when state changes, so logic can be unit tested without the UI.

In practice: A LoginViewModel exposes isLoading and errorMessage; the SwiftUI or Compose screen just shows them.

Related:Dependency injectionDesign pattern

N

Also: N+1 problem

The N+1 problem appears when code loads N records and then accesses an association of each, triggering a separate query per record. It is fast with test data and slow in production, where N is large. Fixes include fetch joins or entity graphs, batch fetching, and selecting exactly the needed columns into DTOs. Counting SQL statements per request in tests catches it early.

In practice: An endpoint that lists 500 authors with their book counts runs 501 queries instead of one grouped query.

Related:Lazy loading

Also: NFRs, Quality attributes

Functional requirements list features; non-functional requirements set the qualities those features must have: how many users, how fast, how reliable, how fresh the data must be, how secure. In system design they drive most decisions, because the same features at a thousand and at a hundred million users need very different designs.

In practice: "The feed loads in under 200 ms for 10 million daily users, and a new post may appear a few seconds late."

Related:Back-of-the-envelope estimateAvailability

Since Android 8, every notification must belong to a channel the app has created, or it isn't shown. The app sets the initial importance, sound and vibration; after that only the user can change them. Channels should match what users want to control separately. Android 13 also requires the POST_NOTIFICATIONS permission.

In practice: A shop creates "Order updates" at default importance and "Offers" at low importance.

Related:Runtime permissionPush notification

Null safety

Language

Also: Optionals, Nullable types

In a null-safe language, the type of a variable says whether it can be empty. Swift calls these optionals (String?), Kotlin calls them nullable types (String?), and Java has the Optional<T> wrapper plus annotations. Before you use a value that might be missing, the compiler makes you handle the empty case, which removes a whole class of crashes.

In practice: A user profile has an optional middle name. In Swift, user.middleName.count does not compile until you unwrap it, for example with user.middleName?.count ?? 0.

Related:Optional chainingType inference

O

Observer pattern

Design patterns

Also: Observer, Publish-subscribe, Listener

The subject keeps a list of observers (also called listeners or subscribers). Observers subscribe to be told about changes and unsubscribe when they no longer care, and the subject calls each of them when something happens. It is the idea behind click listeners, NotificationCenter, Combine, StateFlow, LiveData and Spring application events. The usual bugs are subscriptions that never end and observers called at the wrong time or on the wrong thread.

In practice: A cart badge subscribes to the cart and updates its number whenever an item is added, while the cart only knows it has observers.

Related:Design pattern

Open/closed principle

Design patterns

Also: OCP

The "O" in SOLID. When a new variant (a payment method, a shipping option) arrives, you should be able to add it as new code instead of editing a growing switch statement in several places. Interfaces and patterns like Strategy are the usual way to get there.

In practice: Adding Apple Pay means writing an ApplePayPayment class that implements Payment, while Checkout stays untouched.

Related:Design patternDependency injection

Each row has a version number. An update includes the version it was based on (where id = ? and version = ?) and increments it. If another transaction changed the row in the meantime, no row matches, and the update fails with a conflict that can be retried or shown to the user. In JPA you add a @Version field. It suits low-contention edits; for hot rows, atomic updates or pessimistic locks work better.

In practice: Two admins edit the same product; the second save fails with 409 Conflict instead of silently undoing the first admin's change.

Related:Transactional outbox

Also: Safe call

Optional chaining uses ?. to step through properties or method calls that may be empty. If any link in the chain is missing, the whole expression evaluates to nil (Swift) or null (Kotlin) instead of crashing. It is usually combined with a default value: ?? in Swift, the Elvis operator ?: in Kotlin.

In practice: order?.customer?.address?.city ?? "Unknown" shows "Unknown" when the order has no customer or the customer has no address.

Related:Null safety

P

Also: Consumer group, Offset

A topic is split into partitions so it can be stored on several brokers and read in parallel. The producer hashes the message key to pick a partition, so all events for one key stay in order. Within a consumer group, each partition is assigned to exactly one consumer, which makes the partition count the ceiling on parallelism. Adding partitions later changes which partition a key maps to.

In practice: An orders topic with 12 partitions, keyed by order id, can be consumed by up to 12 warehouse instances in parallel.

Related:Message queueConsumer lag

Pattern matching combines a test and a destructuring. Swift matches enum cases with their associated values (case .failed(let message)), Kotlin checks types in when with smart casts (is Failed ->), and Java 21 supports type and record patterns in switch (case Failed(var message)). Combined with enums or sealed types, the compiler can check that every possible shape is handled.

In practice: case Loaded(var products) when products.isEmpty() -> "No results" matches only loaded states with an empty list.

Related:Sealed hierarchy

Also: Staged rollout

On the App Store, a phased release delivers automatic updates over 7 days (1%, 2%, 5%, 10%, 20%, 50%, 100%) and can be paused; users can still update manually. Google Play's equivalent is a staged rollout with percentages you choose, which can be halted. Neither store can roll users back, so fixes ship as new versions, ideally with remote feature flags to switch off a broken feature in the meantime.

In practice: A crash appears at 5%; the team pauses the release, disables the feature remotely and submits a fix for expedited review.

Related:Provisioning profile

When Kotlin calls a Java method without nullability annotations, it can't tell whether the result may be null. It gives it a platform type (written String! in error messages) and lets you use it as either String or String?. If you treat it as non-null and Java returns null, you get a NullPointerException at runtime. Annotating the Java code (for example with JSpecify) turns platform types into real Kotlin types.

In practice: val name = javaUser.getDisplayName() followed by name.length compiles, but crashes when the Java method returns null.

Related:Null safetySmart cast

Prefix sum

Algorithms

Also: Running sum, Cumulative sum

Building the prefix array takes one pass. After that, the sum of the numbers from index l to r is prefix[r + 1] minus prefix[l], in constant time. Starting the array with a 0 removes special cases for ranges that begin at index 0. Together with a hash map of earlier prefix sums, the idea also counts subarrays with a given sum, even when numbers can be negative.

In practice: For [3, 1, 4, 1, 5] the prefix array is [0, 3, 4, 8, 9, 14], and the sum of indexes 1 to 3 is 9 - 3 = 6.

Related:Hash mapSliding window

Presigned URL

System design

Also: Signed URL

The server signs the bucket, key, HTTP method, expiry and optionally content type, size and checksum. The client uploads or downloads directly with the URL, so file bytes never pass through the API servers. Grants should be narrow: a server-chosen key, a short expiry, and limits on type and size. What arrives must still be verified before it is used.

In practice: The API returns a 15-minute PUT URL for uploads/u42/7f3a; the app sends the photo straight to S3.

Related:Multipart upload

Priority queue

Algorithms

Also: Heap, Min-heap, Max-heap

A priority queue supports adding elements and removing the one with the highest priority, both in O(log n), and peeking at it in O(1). It is usually implemented as a binary heap stored in an array. Java and Kotlin have PriorityQueue (a min-heap by default); Swift uses Heap from swift-collections. Its iteration order is not sorted.

In practice: A scheduler keeps tasks in a priority queue ordered by run time and always starts the earliest one.

Related:QueueBig-O notation

No callback runs before the process is killed. When the user comes back from Recents, Android recreates the Activity and restores saved-instance state (SavedStateHandle, rememberSaveable, the navigation back stack), but ViewModels, singletons and in-memory caches start empty. Screens must be rebuildable from saved ids and persisted data.

In practice: A checkout keeps the cart id in SavedStateHandle and reloads the cart after the user returns from their banking app.

Related:Configuration change

Prototype pattern

Design patterns

The important decisions are how deep the copy goes and what must change in it. Nested mutable objects need to be copied or made immutable, otherwise the original and the copy share them; ids, numbers, dates and status must be reset so the copy is a new object. A prototype registry keeps preconfigured templates and always hands out copies. In Java, copy constructors are preferred over Cloneable.

In practice: Duplicating an invoice copies the client and line items but gets a new id, a new number and draft status.

Related:Shallow copyValue type

Every iOS app and app extension needs one to be signed and installed. Development profiles list the devices a build can run on; App Store distribution profiles allow uploading to App Store Connect. Xcode's automatic signing creates and renews them; CI systems use an App Store Connect API key or tools such as fastlane match.

In practice: The notification service extension needs its own App Store profile, or the archive fails to export.

Related:Phased release

Proxy pattern

Design patterns

A proxy can create the real object only when it's first needed (virtual proxy), check permissions (protection proxy), remember results (caching proxy) or forward calls to another machine (remote proxy). Callers can't tell it from the real object. Frameworks generate proxies for lazy ORM associations and for annotations such as @Transactional, which only work for calls that go through the proxy.

In practice: A LazyDocument shows its title from the file name and reads the file only when content() is called.

Related:Lazy loadingDecorator patternFacade pattern

Push notification

System design

Also: APNs, FCM, Device token

The app registers with the OS and receives a device token, which it sends to your server on every launch. To notify the user, your server sends a small payload and the token to APNs or FCM. Invalid tokens (uninstalled apps) must be deleted when the provider reports them, transient errors retried with backoff, and sensitive data kept out of the payload because it appears on the lock screen.

In practice: "Your order 1042 has shipped" is sent to both of a user's devices; one token returns 410 Unregistered and is removed.

Related:WebSocketIdempotency

Q

Queue

Algorithms

Also: FIFO

A queue adds elements at the back and removes them from the front, both in O(1). It is used for tasks processed in arrival order, message passing and breadth-first search, which explores a graph level by level. Kotlin and Java use ArrayDeque; Swift has Deque in the swift-collections package. Removing from the front of a plain array is O(n), so it is not a good queue.

In practice: Breadth-first search puts the start node in a queue, then repeatedly takes the front node and adds its unvisited neighbors to the back.

Related:Stack

R

R8

Android

Also: ProGuard

R8 removes classes and methods nothing calls, renames what remains to short names, and optimizes the code. It can't see code accessed only by name at runtime through reflection, such as fields filled in by Gson, so those need keep rules or annotations, or a code-generated library instead. The mapping file it produces is needed to turn obfuscated crash stack traces back into real names.

In practice: With isMinifyEnabled = true, an OrderDto parsed by Gson loses its field names unless they are kept.

Related:Baseline profile

In Compose, the UI is produced by calling composable functions. When a State object that a composable read changes, Compose calls that composable again (recomposes it) to produce the updated UI, and skips composables whose inputs didn't change. Values you want to keep across recompositions must be stored with remember.

In practice: Tapping a counter button changes count, so the composable that displays count recomposes and shows the new number.

Related:State hoisting

Recursion

Algorithms

Every recursive function needs a base case that stops the calls and a step that makes the problem smaller. Each call waits on the call stack until the calls it made return, so very deep recursion can run out of stack space. Tree traversals, divide and conquer, and backtracking are naturally recursive.

In practice: factorial(n) returns 1 when n is 0, and n * factorial(n - 1) otherwise.

Related:BacktrackingStack

A variable of a reference type holds a link to an object that lives on the heap. Assigning it or passing it to a function copies the link, not the object, so every variable sees changes made through any of the others. Objects have identity: two objects can hold the same data and still be different objects. Swift classes and actors, and every non-primitive type in Kotlin and Java, are reference types.

In practice: val b = a with a Kotlin class instance, then b.name = "Marko": a.name is now "Marko" too, because a and b are the same object.

Related:Value type

Replication lag

System design

Also: Eventual consistency

With asynchronous replication, followers apply the leader's changes a little later, usually milliseconds, sometimes seconds under load. Reads from a lagging copy can show a user their old data right after they saved, make new items disappear on refresh, or show replies before questions. Read-your-writes, sticky replicas and per-conversation ordering fix the anomalies users notice.

In practice: A user renames their profile, the next screen reads from a replica 2 seconds behind and shows the old name.

Related:ShardingCAP theorem

Repository pattern

Design patterns

A repository offers lookups by id, finders named after what the app needs, and save and delete, and returns the app's own domain models. Behind it, implementations talk to a database, a server or a cache, and on mobile it decides which source answers. Screens and use cases depend only on the interface, so tests use in-memory fakes, kept honest by contract tests run against both the fake and the real implementation.

In practice: OrderRepository.recentOrders(customerId, limit = 20) runs one SQL query instead of loading every order.

Related:Dependency inversion principle

REST API

Backend

Also: REST, RESTful API

REST (Representational State Transfer) is a style for designing web APIs. Each resource, like a book or an order, has a URL; clients use HTTP methods to read (GET), create (POST), replace (PUT), change (PATCH) or remove (DELETE) it, and the server answers with a status code and usually JSON. It's the most common way mobile apps talk to their backends.

In practice: GET /api/books/42 returns book 42 as JSON with status 200, or 404 if it doesn't exist.

Related:HTTP status codeIdempotencyDTO

Result type

Language

Instead of throwing, a function can return a result: Swift's Result<Success, Failure>, Kotlin's Result or a custom sealed type, or a hand-written sealed interface in Java. Results are useful when a failure has to be stored, passed through callbacks or collected, for example in UI state. Kotlin's runCatching catches every Throwable, including coroutine cancellation, so use it carefully.

In practice: A validation step returns a list of results so the form can show every invalid field at once.

Related:Checked exceptionSealed hierarchy

Also: Reference cycle, Strong reference cycle

A retain cycle is the most common memory leak under reference counting. Each object keeps the other alive, so their counts never drop to zero even when nothing else uses them. Mark one side of the relationship as weak (or capture [weak self] in closures) to break it.

In practice: A parent view controller owns a child, and the child keeps a strong delegate reference back to the parent. Closing the screen leaks both.

Related:Automatic Reference Counting

Also: Dangerous permission

Declaring the permission in the manifest or Info.plist isn't enough: the user is asked in a system dialog and can deny it, grant it once, or revoke it later in Settings. Android stops showing the dialog after two denials and iOS shows it only once, so apps ask when the user starts the feature that needs it, explain why, handle every answer and check again before each use.

In practice: The camera permission is requested when the user taps Scan receipt, not on the first screen.

Related:Notification channel

S

Also: App state, ScenePhase

Active means in front and receiving touches; inactive means visible but briefly interrupted (Control Center, the app switcher, a call); background means not visible, with a few seconds before the app is suspended. Suspended apps can be terminated without any callback, so user data is saved when the scene enters the background.

In practice: .onChange(of: scenePhase) saves the draft when the phase becomes .background.

Related:Background task (iOS)

In Hilt, a scope annotation ties a binding to a component's lifetime: @Singleton (the whole app), @ActivityRetainedScoped (an activity across rotation), @ViewModelScoped (one ViewModel), @ActivityScoped. Unscoped bindings create a new instance for every injection. Scope expensive or stateful objects that must be shared, such as HTTP clients and databases, and leave stateless helpers unscoped.

In practice: An unscoped OkHttpClient provider gives every repository its own client and connection pool.

Related:Dependency injection

Also: Sealed class, Sealed interface, Sum type, Tagged union

A sealed class or interface only allows the subtypes declared next to it (in the same module and package in Kotlin, listed in permits in Java). Because the compiler knows every subtype, a when or switch that handles all of them needs no default branch, and adding a subtype makes every incomplete switch fail to compile. Swift gets the same effect with enums that have associated values.

In practice: sealed interface SearchState with Idle, Loading, Loaded(products) and Failed(message): a screen is always in exactly one of them.

Related:Pattern matchingSmart cast

Sendable

Concurrency

The compiler only lets Sendable values cross from one concurrency domain to another. Value types made of Sendable parts are Sendable automatically, as are actors, immutable final classes and classes isolated to the main actor. In the Swift 6 language mode, sending a non-Sendable value across domains is a compile error. @unchecked Sendable turns the check off, so it must be backed by a real lock.

In practice: A struct Order with let properties can be returned from a background task to a @MainActor view model without any extra work.

Related:ActorData race

Also: SSE, EventSource

The client opens a normal HTTP request and the server writes events into the response as they happen. Because it is plain HTTP, it works through proxies and load balancers, and browsers reconnect automatically, sending the id of the last event received in a Last-Event-ID header so the server can send what was missed. It suits live feeds where only the server sends data; WebSockets suit two-way traffic.

In practice: A restaurant's order board receives each new order as an event within a second, and catches up after a reconnect.

Related:WebSocket

Shallow copy

Language

Also: Deep copy

Changing a nested list or object through the copy changes the original too, because there is only one of it. A deep copy also copies the nested objects. Kotlin data class copy(), TypeScript object spread and Java clone() are shallow; Swift structs whose fields are all value types copy deeply. Immutable nested data makes sharing safe.

In practice: copy = original.copy() followed by copy.items.add(extra) adds the line to the original invoice as well.

Related:Prototype patternValue type

Sharding

System design

Also: Horizontal partitioning, Partitioning

When data or traffic outgrows one database server, you split it by a shard key: each shard holds the rows whose key maps to it, often by hashing the key. Lookups by that key go to exactly one shard. Queries that don't use the key have to ask every shard, so choosing the key is the most important decision.

In practice: A URL shortener shards by short code, so every redirect reads from exactly one shard.

Related:CachingLoad balancer

Also: SRP

Code that changes for different stakeholders, such as pricing rules, storage and email wording, should live in different classes, so a change for one cannot break the others. It does not mean one method per class: a class with ten methods that all serve the same job still has a single responsibility.

In practice: An OrderService that validates, prices, saves and emails is split into a pricing policy, a repository and a notifier.

Related:Open/closed principle

Singleton

Design patterns

Also: Singleton pattern

A singleton hides its constructor and exposes one shared instance: Swift static let shared, a Kotlin object, a Java static final field or enum, a TypeScript module export. One instance is often fine (a logger, an HTTP client, a cache); global access is the risky part, because it hides dependencies and shares state between features and tests. Modern code keeps the single instance and passes it in.

In practice: Analytics.shared.track("purchase") works everywhere, but a view model that receives the analytics object in its constructor can be tested with a fake.

Related:Dependency injectionDesign pattern

Sliding window

Algorithms

A window is a range between two indices. A fixed window adds the element that enters and removes the one that leaves. A variable window grows on the right and shrinks from the left while it breaks a rule. Each element enters and leaves once, so the work is O(n). It needs a rule that changes in one direction as the window grows, which is why it works for non-negative sums but not with negative numbers.

In practice: The longest substring without repeating characters: grow the window, and when a character repeats, shrink from the left until it doesn't.

Related:Two pointersHash map

Smart cast

Language

After a check like if (name != null) or if (shape is Circle), Kotlin automatically treats the value as String or Circle inside that block. It only works when the compiler can prove the value can't change between the check and the use, so it works for local vals but not for mutable properties (var) that another thread or a custom getter could change.

In practice: if (user.email != null) sendTo(user.email) fails to compile when email is a var property; user.email?.let { sendTo(it) } works.

Related:Null safetyPlatform type

Also: Bean

Classes marked with @Component, @Service, @Repository or @RestController, and the results of @Bean methods, become beans. Spring creates them at startup, passes them to each other through constructors, and by default keeps exactly one instance of each, shared by every request and thread. That is why beans should not store data about a single request or user in their fields.

In practice: A QuoteController asks for a QuoteService in its constructor, and Spring passes in the single QuoteService bean.

Related:Dependency injectionInversion of control

Stack

Algorithms

Also: LIFO

A stack supports push (add on top), pop (remove from the top) and peek, all in O(1). It is the natural structure for undo, the back button, matching brackets and depth-first traversal. Function calls also use a stack, the call stack, which has a fixed size and can overflow with very deep recursion. Swift uses an Array as a stack; Kotlin and Java use ArrayDeque.

In practice: Checking brackets: push every opening bracket, and pop when a closing one arrives to check it matches.

Related:Queue

A hoisted component is stateless: it gets the current value and a function to call when the user changes it. The caller owns the state. This keeps a single source of truth, makes components reusable and easy to preview and test. The rule of thumb is: values go down, events go up. It applies to Jetpack Compose and SwiftUI alike.

In practice: SearchField(query = query, onQueryChange = { query = it }) lets the screen own the text while the field just displays it.

Related:Recomposition

State machine

Design patterns

Also: Finite state machine, FSM, State pattern

Instead of several booleans that can combine into impossible situations, a state machine has one current state and a transition function (state, event) to next state. Every rule lives in one place, unexpected events in a state become a deliberate decision, and transitions can be tested as a table. The State pattern implements it with one object per state.

In practice: A media player is idle, loading, playing, paused or failed; tapping Play while loading does nothing, by rule.

Related:Sealed hierarchyUnidirectional data flow

With structured concurrency, background work is started inside a scope: a Kotlin CoroutineScope, a Swift task group or async let. The scope can't finish until its children finish, cancelling the scope cancels the children, and a child's failure is reported to the parent. Work therefore can't leak past the screen or request that started it. Unstructured tasks such as GlobalScope.launch or Task.detached opt out of these guarantees.

In practice: viewModelScope cancels every coroutine it started when the ViewModel is cleared, including in-flight network requests.

Related:Coroutineasync/await

Sweep line

Algorithms

Instead of comparing every interval with every other one, you turn each interval into a start event and an end event, sort all events by time, and walk through them once. A counter or a heap holds what is currently open. It answers questions like the maximum number of overlapping meetings in O(n log n).

In practice: Meetings 9-10, 9:30-11 and 10:30-12 give events at 9 (+1), 9:30 (+1), 10 (-1), 10:30 (+1), 11 (-1), 12 (-1); the count peaks at 2.

Related:Priority queue

Introduced with Xcode 16, Swift Testing replaces XCTestCase subclasses and XCTAssert functions for unit tests. #expect records a failure and shows the values involved; #require stops the test. Tests run in parallel by default, suites are structs with a fresh instance per test, and @Test(arguments:) reports each input separately. UI tests and performance tests still use XCTest.

In practice: @Test(arguments: ["", "a", "abc"]) runs one validation test three times, with three separate results.

Related:Unit testTest double

T

The template method itself is final; it calls abstract methods (steps every subclass must implement) and hooks (optional steps with a default). Framework lifecycle methods such as viewDidLoad and onCreate are steps in templates owned by the framework. When variants need to be combined or chosen at runtime, passing the varying step as a function or Strategy is usually better than inheritance.

In practice: ContactImporter.run parses, validates, saves and reports; CsvContactImporter only implements parse.

Related:Design pattern

Also: Fake, Stub, Mock, Spy

Test doubles replace dependencies that are slow, random or external. A fake has a simple working implementation (an in-memory store), a stub returns prepared answers, a spy records how it was called, and a mock verifies expected calls. They are passed in through the constructor, which is why code designed with dependency injection is easy to test.

In practice: A FakeGateway that can be told to fail lets a test check the "payment failed" screen without a real card.

Related:Unit testDependency injection

Test slice

Backend

@WebMvcTest loads controllers, filters and JSON mapping; @DataJpaTest loads entities, repositories and a database; @JsonTest loads only serialization. Beans outside the slice aren't created, so they're replaced with mocks (@MockitoBean). Slices sit between plain unit tests and full @SpringBootTest integration tests.

In practice: A @WebMvcTest checks that POST /orders with an invalid body returns 400 with a problem detail, without a database.

Related:TestcontainersSpring bean

Instead of an in-memory substitute like H2, integration tests run against the same database engine and version as production. Containers start before the tests, Spring Boot connects to them through @ServiceConnection, and they're thrown away afterwards. Sharing one container and one Spring context across test classes keeps the suite fast.

In practice: A repository test starts postgres:17 and checks that a native query with a JSONB operator really works.

Related:Test slice

Also: TCA

Each feature's reducer changes state in response to actions and returns effects for asynchronous work, which send more actions when they finish. Dependencies are resolved through the library so tests and previews can replace them, and TestStore checks every state change and received action. It brings strong consistency and testability at the cost of boilerplate and a framework that shapes the whole codebase.

In practice: Tapping favorite sets isFavorite = true immediately and rolls it back when the saveResponse action reports a failure.

Related:MVIUnidirectional data flow

Token bucket

System design

A bucket holds up to a fixed number of tokens and refills at a constant rate. Each request takes a token; when the bucket is empty, requests are rejected until tokens drip back in. The bucket size sets the largest burst, and the refill rate sets the long-term limit. It needs only two numbers per client: the token count and the time of the last refill, which is calculated on each request rather than by a timer.

In practice: A bucket of 10 tokens refilled at one token every 6 seconds lets a quiet user send 10 requests at once, then about 10 per minute.

Related:Load balancer

Tombstone

System design

In sync and replication, simply removing a row leaves nothing for other copies to receive, so they keep the item and may even restore it. A tombstone is a soft-deleted row or a delete event in the change feed that is kept for a retention period. Devices that have been offline longer than that period must do a full resync.

In practice: Deleting a note sets isDeleted = true; every device sees the change on its next sync and removes the note from its local database.

Related:Idempotency

In a topological order, every node comes after all the nodes it depends on. It exists only when the directed graph has no cycles. Kahn's algorithm repeatedly takes nodes with no remaining incoming edges; if some nodes are never taken, they are part of a cycle. Build tools, package managers and course planners rely on it.

In practice: With "course 0 before 1" and "course 1 before 2", a valid order is 0, 1, 2.

Related:Queue

Also: Outbox pattern

A database and a message broker can't share one transaction, so publishing an event directly can lose it or send it for a change that was rolled back. With an outbox, the event is inserted into an outbox table in the same transaction as the data. A separate relay reads unsent rows and publishes them, marking them as sent. Delivery is at least once, so consumers must handle duplicates.

In practice: Saving an order also inserts an OrderPlaced row into the outbox; a relay publishes it to Kafka a moment later, even if the app restarted in between.

Related:Idempotency

Two pointers

Algorithms

Instead of checking every pair of elements, two indices move through the data once. From both ends, they solve palindrome checks and pair-sum problems on sorted arrays. In the same direction, one reads and one writes, which removes or compacts elements in place. Each move must rule out candidates that can't be the answer, which usually requires sorted input.

In practice: To find two numbers in a sorted array that add up to a target, start at both ends: move the left pointer right when the sum is too small and the right pointer left when it is too big.

Related:Big-O notationSliding window

Also: Type coercion, Casting

Some conversions are written explicitly (Double(x) in Swift, x.toDouble() in Kotlin, a cast in Java), and some happen automatically (Java widens int to double; JavaScript turns values into strings or numbers when operators need them). Converting text to a number can fail, which each language reports differently: nil, null, an exception or NaN.

In practice: Int("42") is 42 in Swift, but Int("42 years") is nil, and Number("") is 0 in TypeScript.

Related:Type inference

Type erasure

Language

Java and Kotlin check generic types at compile time and then erase the type arguments in the compiled code. At runtime, code can't ask whether a list is a List<String>, can't create a T, and libraries need extra help (TypeToken, TypeReference, reified type parameters) to know the full type. Swift keeps generic type arguments at runtime instead.

In practice: new ArrayList<String>().getClass() == new ArrayList<Integer>().getClass() is true in Java.

Related:GenericsVariance

With type inference, let count = 3 in Swift, val count = 3 in Kotlin and var count = 3 in Java all create an integer without spelling out Int or int. The variable is still strictly typed: you just didn't have to write the type. You can always add it when it makes the code clearer.

In practice: val names = listOf("Ana", "Marko") is inferred as List<String>, so names.add(42) is a compile error.

Related:Generics

U

Also: UDF

In unidirectional data flow (UDF), one state holder, usually a ViewModel, owns the screen's state and exposes it as a read-only, immutable value. The UI only displays that state and reports what the user did as events. The owner handles each event, produces a new state, and the UI renders it again. Because there is a single owner and a single direction, the screen can't get into states nobody created on purpose.

In practice: Tapping Add calls viewModel.onAction(AddClicked); the ViewModel saves the item and emits a new UiState, which the screen collects and shows.

Related:MVVMState hoisting

A unit test arranges input, acts by calling the code once, and asserts that the result matches a value worked out from the requirement. Unit tests run in milliseconds without network, database or real clock, so the whole suite can run after every change. Swift Testing, JUnit and Vitest are common frameworks.

In practice: A test checks that discountedCents(3000, percent: 10) returns 2700, and that 4999 cents of shopping still costs 499 cents of shipping.

Related:Test doubleDependency injection

Use case

Architecture

Also: Interactor

A use case usually has a single public function (in Kotlin often operator fun invoke) and depends on repositories or other use cases. It is worth adding when a rule is shared or complex enough to test on its own; a use case that only forwards a call to a repository adds a file without adding value.

In practice: FreeShippingUseCase decides whether a cart qualifies for free shipping, for both the cart and the checkout screens.

Related:Clean Architecture

V

Value type

Language

Also: Value semantics

When you assign a value type to a new variable or pass it to a function, the data itself is copied. Changing the copy never changes the original, and two equal values are interchangeable. In Swift, struct, enum, tuples, String and the standard collections are value types. On the JVM only primitives (int, double, boolean and so on) are; Kotlin data classes and Java records are reference types that can behave like values when they are immutable.

In practice: var b = a with a Swift struct gives b its own copy: b.name = "Marko" leaves a.name unchanged.

Related:Reference typeCopy-on-write

Variance

Language

If Cat is an Animal, a read-only list of cats can safely be used as a list of animals (covariance), but a writable one can't, or a Dog could be added to it. Kotlin declares variance on types with out and in, Java uses wildcards at each use (? extends to read, ? super to write), and Swift generics are invariant except for the built-in collections.

In practice: Kotlin's List<out E> is covariant, so a List<Cat> can be passed where a List<Animal> is expected, while MutableList<Cat> can't.

Related:GenericsType erasure

Also: Modifier

Each modifier returns a new view that contains the previous one, so a chain of modifiers is applied from the inside out and its order changes the result. Some modifiers change layout, some draw, and some set environment values like the font that flow down to every child view. Jetpack Compose has the same idea in its Modifier chain, read from the outside in.

In practice: Text("Go").padding().background(.blue) paints the padding blue; Text("Go").background(.blue).padding() does not.

Related:Declarative UI

Virtual thread

Concurrency

Virtual threads (Java 21+) run on a small pool of operating-system carrier threads. When a virtual thread blocks on I/O or sleeps, the JVM unmounts it and runs another one on the same carrier, so ordinary blocking code can serve tens of thousands of concurrent tasks. They don't make code faster or prevent data races, and they move the bottleneck to limited resources like connection pools.

In practice: Executors.newVirtualThreadPerTaskExecutor() can run 10,000 tasks that each sleep one second in about one second.

Related:Coroutineasync/awaitStructured concurrency

Also: Screen reader, TalkBack

VoiceOver users move between elements by swiping and activate them with a double tap. Apps help by giving icon-only controls labels, hiding decorative images, combining the parts of a row into one element, using real buttons rather than tap gestures, and offering secondary actions through the actions rotor. Android's equivalent is TalkBack.

In practice: A heart button with accessibilityLabel("Add to favorites") is read as "Add to favorites, button".

Related:Dynamic Type

W

Also: weak, WeakReference, WeakRef

A normal (strong) reference keeps its object in memory. A weak reference doesn't count: when nothing else holds the object, it is freed and the weak reference becomes nil (Swift weak), null (Java and Kotlin WeakReference) or undefined (JavaScript WeakRef). In Swift, weak references are the standard way to break retain cycles, for example for delegates and closures that use self.

In practice: weak var delegate: DownloadDelegate? lets a downloader call back to its screen without keeping the screen alive.

Related:Retain cycleAutomatic Reference Counting

WebSocket

System design

A WebSocket starts as an HTTP request and is upgraded to a persistent connection over which both sides send messages at any time. Chat, live updates and multiplayer games use them. At scale, connections live on a fleet of gateway servers, so routing a message to a user needs a shared registry or pub/sub, and anything sent over the socket must also be stored so a reconnecting client can catch up.

In practice: A chat gateway holds 100,000 open WebSockets and pushes each new message down the recipient's connection.

Related:Push notification

A Worker holds the work and a WorkRequest says when it may run: constraints such as network or charging, a backoff policy for retries, and input data. WorkManager stores requests in its own database and schedules them within Android's battery rules, so they survive process death and reboots. Unique work prevents duplicates; expedited and foreground work cover urgent or long user-visible jobs.

In practice: A review is saved locally and an UploadReviewWorker with a CONNECTED constraint sends it whenever the phone is online.

Related:Background task (iOS)Coroutine

Esc

Type to search the whole site.