cswork / libraryOpen practice workspace

Interview guides · Brickred Systems

Brickred Systems Software Engineer Interview Guide

Brickred Systems Software Engineer interview guide: reported array, complexity and OOP questions, a 7-day plan and behavioral prep for the loop.

The 60-second brief

Candidates describe Brickred Systems as a company that delivers software solutions to a varied set of clients. A Software Engineer there is reported to work across the development lifecycle: analysing requirements, designing, building, testing and maintaining systems, reviewing teammates' code and keeping documentation current. The reported setup is a geographically dispersed team, with daily stand-ups or project syncs used to stay aligned. No specific product, client or internal technology is named in what candidates report, so this guide concentrates on the fundamentals that are named.

This guide is for candidates interviewing for a Software Engineer position at Brickred Systems, including early-career applicants. It covers the reported question types (array manipulation, complexity analysis, algorithm explanation, inheritance and polymorphism, encapsulation, abstract classes versus interfaces), the reported expectations around communication and teamwork, and a plan for working through them. The role asks for one major language such as Java, C++ or Python, so choose the one you code in most fluently and use it throughout.

Candidates report technical rounds on core engineering skills, followed by discussions that can involve leadership or stakeholders in other time zones. A full process of at least two to three weeks is reported, and no round-by-round sequence is published, so confirm the format with the recruiter.

Focus areas

  • Data Structures (DSA)
  • Object-Oriented Programming (OOP)
  • Algorithms (Problem Solving)

Overview

The reported questions fall into two families. The first is coding and algorithms: a medium-level array manipulation problem, a follow-up about improving the time complexity of the solution you just wrote, and a request to explain the logic of a specific algorithm. The second is object-oriented design: build a class structure that shows inheritance and polymorphism, describe how you handle encapsulation, and explain abstract classes versus interfaces in a system-design setting. Candidates also report behavioral questions about past experience, conflict and teamwork.

For the coding family, the reported emphasis is readable code, edge-case handling and being ready to discuss Big O. In practice that means a fixed routine for every array problem: state the brute force and its cost, name the pattern that removes the repeated work (hash map, two pointers, sliding window, prefix sums, sorting plus a scan), write it, then test null or empty input, a single element, duplicates and extreme values. The optimization question is the second half of that routine, so practise saying the before and after complexity for each solution you write.

For the design family, candidates are told to be able to justify a class hierarchy or pattern, and to know SOLID plus Singleton, Factory and Strategy. Prepare one small domain you can model quickly (shapes, payment methods, notification channels), and rehearse where inheritance helps, where composition is the better choice, and why an interface or an abstract class fits a given case. Because the team is reported as distributed across time zones and communication is listed as a requirement, practise explaining each decision aloud as you write it, and keep a few resume projects ready to discuss in detail.

Where to focus your preparation

Candidates report a series of technical rounds followed by discussions that may include leadership or stakeholders in other time zones. No named stages are published, so the single entry below groups the preparation.

Preparation focus

Candidates report technical rounds on fundamentals first, then conversations that can involve leadership or stakeholders in other time zones, over a process of at least two to three weeks. Scheduling can change quickly and interviews may fall at late or early hours depending on where interviewers are based. The reported question types are medium-level array problems with complexity discussion, algorithm explanation, class design with inheritance and polymorphism, encapsulation, and abstract classes versus interfaces, plus behavioral questions on experience, conflict and teamwork. Because the sequence is not published, ask the recruiter which stages exist and whether you will write code, then rehearse both coding aloud and whiteboard-style class design.

What to demonstrate

  • Working code for a medium array problem, with complexity stated and edge cases (null, empty, extreme values) handled
  • Ability to explain why one class hierarchy or design pattern fits better than another
  • Understanding of OOP concepts such as inheritance, polymorphism, encapsulation and abstract classes versus interfaces
  • Clarity of spoken reasoning and teamwork examples drawn from your own projects

How to prepare

  • Solve array problems in your chosen language, such as pair sums, subarray sums, merging ranges and removing duplicates, then write the brute-force and optimized complexities beside each
  • Model one hierarchy end to end, for example a base Shape or PaymentMethod with subclasses that override a method, and be ready to explain where polymorphism is used
  • Write a one-paragraph answer on abstract class versus interface and another on encapsulation, each with a code example you can sketch from memory
  • Prepare two resume projects with your own contributions and one conflict or code-review story, and reply to recruiter messages promptly so scheduling changes do not cost you a slot

Where candidates lose points

Jumping to code on the array problem without stating the brute force or asking about input size, duplicates and negatives

Spend the first minute restating the input, naming the simple O(n^2) approach and then the pattern that improves it. Say your assumptions out loud before typing a line.

Producing a working solution but being unable to say its time and space complexity, or what changes when the optimization follow-up arrives

After every practice solution write both complexities and the single idea that removed the extra work. Practise the sentence: it was quadratic because of X, and a hash map or two pointers brings it to linear at the cost of extra space.

Skipping the empty array, single element, all-equal values and extreme numbers, so the code fails on a case an examiner can name in seconds

Keep a fixed edge-case checklist (null, empty, one element, duplicates, negatives, overflow) and run each case through your code by hand before declaring it finished.

Describing inheritance, polymorphism or abstract classes only as definitions, with no class structure drawn or written to back them up

Memorize one small hierarchy and its code. Show a base type with an overridden method called through the base reference, and explain when you would pick an interface (a capability several unrelated classes share) over an abstract class (shared state and partial behaviour).

Staying silent while thinking, or answering a conflict or teamwork question with a general principle instead of a real project

Practise coding with a running spoken commentary, and prepare two resume projects plus one conflict story where you can state your specific contribution and what changed afterwards.

Questions & practice

Write a function to solve a medium-level array manipulation problem.

Medium · Coding and Algorithms

Approach

  1. Pin down the contract before coding: in place or a new array, whether values can be negative, whether duplicates exist, and what to return for an empty input. These answers pick the technique. A sliding window on sums, for example, only works when every value is non-negative.
  2. Match the problem to a pattern. Two pointers on a sorted array (pair sum, removing duplicates) runs in O(n). A sliding window finds the longest or shortest contiguous range under a monotone constraint in O(n). Prefix sums plus a hash map of prefix counts handle 'subarrays summing to k' with negatives in O(n) time and O(n) space. A write index compacts in place (move zeros, remove a value) in O(n) time with O(1) extra space.
  3. State the brute force first: nested loops over all pairs or subarrays, O(n^2). Then name the invariant the faster version keeps, for example 'everything left of the write index is final output' or 'the window never contains a repeated value'.
  4. For rotation-style questions, rotate right by k in place: take k mod n, reverse the whole array, then reverse the first k elements, then reverse the remaining n-k. That is O(n) time and O(1) space.
  5. Test edge cases out loud: a null or empty array, one element, all values equal, all negative values, k = 0 or k >= n. In Java or C++, a running sum can overflow a 32-bit int, so use long.

Follow-up

  • If the array can now contain negative numbers, does your sliding window still hold, and what replaces it?
  • Can you do this with O(1) extra space, and what does that cost in time or in changing the input?
  • How would you handle the same problem if the data arrived as a stream you can read only once?

How would you optimize the time complexity of your current solution?

Medium · Coding and Algorithms

Approach

  1. State the current complexity exactly and point at the dominant term. Usually it is an inner loop that repeats a linear search or recomputes a sum, which makes the whole thing O(n^2).
  2. Remove the repeated work. A hash set or map turns the inner lookup into average O(1) and the total into O(n), at the cost of O(n) memory. Prefix sums make each range sum O(1) after an O(n) build. Sorting once in O(n log n) allows two pointers or binary search afterwards.
  3. Reach for the right structure for the shape of the query. A heap of size k gives top-k in O(n log k) instead of a full O(n log n) sort. If the feasibility check is monotone in the answer, binary search on the answer. A monotonic stack gives 'next greater element' in O(n).
  4. Name the trade-off you accepted. You spend memory to save time. A hash map degrades to O(n) per operation under heavy collisions. Sorting loses the original indices, so carry (value, index) pairs when the output needs positions.
  5. Check the faster version against the brute force on small random inputs and on the edge cases before claiming it is correct. Then restate the final time and space complexity.

Follow-up

  • Your hash map version uses O(n) extra memory. Can you reach O(1) extra space, and what do you give up?
  • What is the worst case for your hash-based solution, and what input would trigger it?
  • Is there a reason this cannot be done faster than O(n log n), or can you get to O(n)?

Describe the logic behind your approach for this specific algorithm.

Medium · Coding and Algorithms

Approach

  1. Open with the idea in one sentence, then state the invariant that holds on every iteration. For binary search: 'if the target exists, it lies in [lo, hi]'. For a sliding window: 'the window holds no repeated value'.
  2. Trace a small example by hand, including one edge case, and say how each state variable (pointers, window bounds, map contents) changes at each step.
  3. Argue correctness in the form the algorithm needs. For a loop: the invariant holds at the start, each iteration preserves it, and when the loop ends it gives the answer. For greedy: an exchange argument. For dynamic programming: the state definition, the recurrence and the base cases.
  4. Justify the complexity rather than just stating it. For a sliding window with a nested while loop, each index enters and leaves the window at most once, so the total is O(n) amortized, not O(n^2). Give the extra space separately.
  5. Say which alternative you rejected and why. For example, sorting would make the problem easier but loses the original order, or recursion would risk stack depth on large inputs.

Follow-up

  • Why does your loop terminate, and what happens when lo equals hi or the window becomes empty?
  • Where would this approach break if the input were unsorted or contained duplicates?
  • Which test cases would you write to convince yourself this is correct?

Denormalise tenant onto revisions and backfill it live

Hard · denormalisation · online migration · backfill

resource_revision (revision_id, resource_id, version, actor_user_id, change_kind, patch, request_id, created_at) has 400M rows and no tenant column; tenant_id lives only on resource. Two reads need it: a tenant-scoped audit feed ordered by created_at DESC, and an offboarding purge. Both join back to resource today. Justify adding tenant_id to resource_revision against those two reads, name the anomaly the copy introduces and the constraint that prevents it, then give the ordered migration for a live table taking 1.2k writes/second — the lock each step takes, how the backfill is batched, and where each step stops being reversible. PostgreSQL 16.

Approach

  1. Justify from the access path rather than from taste. Without the column, the audit feed either scans resource_revision by created_at and discards other tenants' rows, or resolves the tenant's resource_ids first and probes with them — both proportional to the tenant's whole history rather than to one page. With (tenant_id, created_at DESC, revision_id DESC) it is a seek that stops at 50 rows, and the purge becomes a ranged delete instead of a join.
  2. Name the cost exactly: a second copy of a fact can disagree with the first. Make the disagreement unwritable rather than documented — add UNIQUE (resource_id, tenant_id) on resource so it can serve as a foreign-key target, then FOREIGN KEY (resource_id, tenant_id) REFERENCES resource (resource_id, tenant_id) on the revision table. A revision can then only ever carry its parent's tenant.
  3. Step one, expand: ALTER TABLE resource_revision ADD COLUMN tenant_id BIGINT NULL, with no default, so it is a catalogue change and no rewrite. It still needs ACCESS EXCLUSIVE for an instant, and that instant queues behind the longest open transaction on the table while every later query queues behind it — set lock_timeout to 2s and retry rather than wait.
  4. Step two, dual-write: deploy the writer that populates tenant_id on every new revision while reads still use the join. Reversible by redeploying the previous build, because nothing reads the column yet.

Follow-up

  • The backfill is half finished and a rollback is required. What state is the table in, and what does the previous build do with a half-populated column?
  • How do you verify the backfill actually finished, given rows are still being inserted while it runs?

Replace offset paging on the resource feed with keyset

Easy · keyset pagination · composite index · sort order

resource holds resource_id, tenant_id, owner_user_id, title, body_ref, version, status ('draft','active','archived','deleted'), created_at, updated_at, deleted_at, with an index on (tenant_id, status, updated_at DESC, resource_id DESC). The listing endpoint returns active resources for one tenant, newest update first, 50 per page, today with LIMIT 50 OFFSET n. Tenants reach page 400 and rows are created while they read. Write the keyset query, define what the cursor carries and how it is encoded, and say which part of the index each predicate uses. Assume PostgreSQL 16.

Approach

  1. Name the two failures separately. OFFSET 20000 makes the server produce and discard 20,000 rows, so page cost grows with depth rather than with page size. Independently, any write that changes how many rows sort above the offset moves the window between two fetches, and the direction decides which anomaly you get: an insert lands at the head of updated_at DESC and pushes already-returned rows down past the boundary, so they are returned a second time; a delete above the offset, or a row whose updated_at is bumped above the cursor, pulls rows up and one is never returned at all. Nothing in the response reveals either.
  2. Write the seek: WHERE tenant_id = $1 AND status = 'active' AND (updated_at, resource_id) < ($2, $3) ORDER BY updated_at DESC, resource_id DESC LIMIT 50. The row-value comparison is one index range rather than a disjunction, and both columns are NOT NULL, which is what makes that comparison well defined.
  3. Map each predicate onto the index: tenant_id and status are equality on the leading columns, (updated_at, resource_id) is the range, and the ORDER BY matches the index order so no Sort node appears and the scan stops after 50 rows. The DESC in the definition only matters for mixed directions — a plain ascending btree on the same columns is read backwards for this query.
  4. Put both sort columns in the cursor and nothing the client can tamper with into another tenant: base64 of (updated_at, resource_id), validated server-side, with tenant_id taken from the principal.

Follow-up

  • The client asks for 'jump to page 400'. What do you offer instead, and what does the honest version cost?
  • Sort order becomes user-selectable across four columns. How many indexes is that, and which would you refuse to add?

Create a class structure that demonstrates the use of inheritance and polymorphism.

Medium · Object-Oriented Programming (OOP)

Approach

  1. Pick a small domain with a real is-a relationship and one behaviour that varies. Example: an abstract Employee with calculatePay(), and SalariedEmployee and HourlyEmployee each overriding it. A payroll loop over List<Employee> then calls calculatePay() with no type checks or instanceof. That loop is the polymorphism.
  2. Put shared state and behaviour in the base class: id, name, private fields with accessors. Declare the varying method abstract so every concrete subclass must implement it. Mark methods that must not be overridden as final in Java.
  3. Separate runtime polymorphism (overriding, resolved by dynamic dispatch) from compile-time polymorphism (overloading). In C++, overriding needs the base method declared virtual, and the base needs a virtual destructor when objects are deleted through a base pointer. In Python, use abc.ABC with @abstractmethod.
  4. Check the Liskov substitution principle. A subclass must honour the base contract: no stricter preconditions, and no throwing on an inherited method. Square extending Rectangle breaks this, because setting the width also changes the height.
  5. Say where composition fits better. If behaviour varies along two independent axes (pay schedule and tax rule, say), inject a strategy object instead of multiplying subclasses for every combination.

Follow-up

  • Add a contractor who is paid per invoice. Which classes change, and which existing ones stay untouched?
  • When would you replace this inheritance hierarchy with composition?
  • In C++, what happens if the base destructor is not virtual and you delete a subclass object through a base pointer?

Explain the difference between an abstract class and an interface in the context of system design.

Medium · Object-Oriented Programming (OOP)

Approach

  1. Start with the Java language difference. An abstract class can have instance fields, constructors, non-public members and concrete methods, and a class extends only one class. An interface has no instance state (its fields are implicitly public static final), and a class can implement many interfaces. Since Java 8 interfaces allow default and static methods; since Java 9, private methods.
  2. Then the design meaning. An interface is a contract or capability at a boundary ('can persist', 'can send a notification'). An abstract class is a partial implementation shared by a closely related family, often through the template method pattern.
  3. Apply it to system design. Make services depend on interfaces at module boundaries (dependency inversion), for example a Repository or PaymentGateway interface. Implementations can then be swapped or mocked in tests. Shared code such as retries or logging can sit in an abstract base under that interface, the way Java's List interface pairs with AbstractList.
  4. Cover the evolution trade-off. Adding an abstract method to an interface breaks every implementer unless you give it a default. Adding a concrete method to an abstract class costs subclasses nothing, but it uses up their single superclass slot and couples them to base-class internals (the fragile base class problem).
  5. Know the other languages. C++ has no interface keyword: you write a class with only pure virtual functions plus a virtual destructor. Python uses abc.ABC with @abstractmethod, or typing.Protocol for structural typing. C# 8 and later allow default interface methods.

Follow-up

  • Now that Java interfaces can have default methods, when do you still need an abstract class?
  • A class implements two interfaces that both define the same default method. What happens, and how do you resolve it?
  • How does depending on an interface rather than a concrete class change how you unit test the service that uses it?

Exports duplicate a row range about once a week

Hard · leases · clock skew · idempotency

Roughly once a week an export writes a file containing a duplicated range of rows. The affected job_run rows show attempt = 1, status = succeeded, one started_at, and a lease_owner naming a different host from the one whose logs show the job starting. Leases last 30 seconds and are heartbeated every 10 from inside the handler; lease_expires_at is computed on the worker and compared against the database's now(). Find the mechanism, and give a fix that holds even if you cannot fix the clocks.

Approach

  1. Start from the fact that eliminates the obvious answer. attempt = 1 means no retry was recorded, so this is not a re-run after failure; two workers ran the same row concurrently and the takeover path never touched the counter. lease_owner naming a host other than the one that started the job is the same statement from the other side.
  2. Enumerate the mechanisms that cause a premature takeover, then find the signal that separates them. Either the lease genuinely expired because the heartbeat did not fire, which is what happens when the heartbeat runs on the handler's own thread and the handler makes a long blocking call, or it only appeared expired because two clocks disagree, since lease_expires_at is written from the worker's clock and evaluated against the database's. The discriminator is the distribution: incidents clustered on the longest exports indict the heartbeat, incidents clustered on one host indict skew. Measure both, and measure each host's offset against the database directly.
  3. Read the reclaim query precisely. In PostgreSQL now() is transaction start time, not statement time, so a reclaimer holding a long transaction compares against an older timestamp than expected; clock_timestamp() is the statement-time function. This is worth ruling in or out before you redesign anything, because it changes which rows look expired.
  4. Remove the second clock rather than trying to synchronise it. Issue and extend the lease in the database, with lease_expires_at = now() + interval '30 seconds' in both the claim and the heartbeat, so exactly one clock is ever compared and worker skew stops mattering to this predicate.

Follow-up

  • The displaced worker has already streamed half the file to object storage. What makes that side effect safe to repeat?
  • You now count takeovers. What alert fires on that counter, and at what threshold?

Suggested practice checklist

The plan follows the reported question families: array coding and complexity first, then algorithm explanation, object-oriented design, encapsulation and interfaces, and finally behavioral stories and a full mock. Each day ends with something you can produce and review.

Day 1 — Confirm logistics and set up array patterns

  • Ask the recruiter which stages exist, whether you will write code, which language is expected and what time zone the interviewers are in
  • Pick one language from the listed set (Java, C++ or Python) and write a one-page cheat sheet of array patterns: hash map lookup, two pointers, sliding window, prefix sums, sort then scan
  • Solve two easy-to-medium array problems and write the brute-force and optimized complexity beside each

Deliverable: A recruiter message with the format answers, a pattern cheat sheet, and two solved array problems annotated with their complexities.

Day 2 — Array problems and the optimization follow-up

  • Solve three medium array problems from scratch, writing the brute force first and then the improved version
  • For each, write one sentence stating what repeated work the optimization removed and what extra memory it uses
  • Run an edge-case checklist (null, empty, single element, duplicates, negatives, large values) on every solution

Deliverable: Three solutions with before and after complexities, an optimization sentence for each, and a checklist of cases you tested.

Day 3 — Explain an algorithm out loud

  • Choose two algorithms you solved this week and record yourself explaining the logic from input to output without looking at the code
  • Replay the recording and mark every place you skipped a step or used a vague word like 'basically'
  • Re-record with a worked example on a small array so the logic is visible, not just described

Deliverable: Two recorded explanations, a marked list of gaps and a cleaner second take of each.

Day 4 — Inheritance and polymorphism in a class design

  • Design a small hierarchy such as Shape with Circle and Rectangle, or PaymentMethod with card and bank subclasses, with a base-class method each subclass overrides
  • Write a short driver that calls the method through the base type on a mixed list, and explain which implementation runs and why
  • Write down one place where composition would be better than inheritance for the same problem

Deliverable: A runnable class hierarchy with a driver and a written paragraph comparing inheritance and composition for it.

Day 5 — Abstract class versus interface, encapsulation, SOLID and patterns

  • Write a comparison of abstract class and interface covering shared state, multiple inheritance, default methods and when you would choose each in a system you are designing
  • Refactor the day 4 hierarchy so its fields are private, its constructor validates input and no getter leaks a mutable collection
  • List the five SOLID principles with a one-line example each, and sketch Singleton, Factory and Strategy for the same domain

Deliverable: A written abstract-versus-interface comparison, a refactored encapsulated class, and a one-page SOLID and patterns sheet.

Day 6 — Behavioral stories and resume walk-through

  • Write STAR outlines for a conflict, a teamwork example and an encapsulation or refactoring story from your own code
  • Prepare a detailed walk-through of two resume projects: your contribution, the hardest technical problem, and what you would change

Deliverable: Three STAR outlines, two project walk-throughs and spoken answers to the two practice exercises.

Day 7 — Full mock and questions to ask

  • Run a mock in one sitting: a medium array problem with an optimization follow-up, then a class design with an abstract class or interface choice
  • End the mock with the encapsulation question and one conflict story, speaking throughout
  • Write two questions to ask interviewers about team structure and how work is shared across time zones

Deliverable: A completed mock with notes on the weakest part and a list of two questions to ask.

Behavioral and teamwork preparation

Candidates report behavioral questions about past experience, conflict and working within a team, and describe the loop as involving collaboration across time zones and code reviews. Prepare stories where your own decision is clear, the outcome is concrete, and you can describe how you communicated with teammates.

  1. Tell me about a time you disagreed with a teammate about a technical approach and how it was resolved.
  2. Describe a project on your resume where you made the key design decision, and what you would change now.
  3. Tell me about a time you gave or received critical feedback in a code review.
  4. Describe working with people in a different time zone and how you kept work moving.

How do you handle encapsulation in your code?

Medium · Object-Oriented Programming (OOP)

Approach

  1. Start with a precise definition: encapsulation means keeping an object's state private and letting only its methods change it, so the class can enforce its own rules and change its internal representation without breaking callers. Say plainly that private fields with a getter and setter for every one of them is not enough.
  2. Anchor the answer in a concrete example from your own code. For instance, an Account or Order class where the constructor validates its arguments, balance or line items are private, deposit and withdraw check invariants (no negative amount, no overdraft), and there is no raw setter for the total.
  3. Show the common leak and its fix: a getter that returns the internal list lets callers mutate state behind the class's back. Return an unmodifiable view or a copy (List.copyOf or Collections.unmodifiableList in Java, a tuple or copy in Python, a const reference or copy in C++), and prefer immutable value objects where possible.
  4. Name the language mechanisms you actually use: private, protected and package-private access in Java, private members and const methods in C++, underscore naming plus @property in Python where access is by convention. Add that exposing behaviour (account.transfer(to, amount)) beats exposing data and letting callers do the arithmetic.
  5. Because this appears as a behavioral question, finish with a short story: a time you found internals exposed, tightened the class, and changed its representation later with no caller edits. Give the situation in two sentences and spend the rest on the decision and the result. The mistake that sinks the answer is reciting a textbook definition with no real code behind it.

Follow-up

  • Why is a getter that returns a mutable internal collection a violation of encapsulation, and how would you fix it?
  • How is encapsulation different from abstraction, and how are they related?
  • Tell me about a refactor where hiding a field's representation let you change it safely. What did you have to keep stable?

Tell callers you do not own that their integration breaks

Medium · deprecation · compatibility · stakeholders

A field in a write endpoint's response must change shape. You own the endpoint; you do not own the four internal callers or the outbound webhook consumers who read it. Describe a deprecation you were responsible for: what you shipped first, how you established who was actually reading the field, the window you gave and what set its length, what you did about the consumer who never moved, and how you decided removal was safe. Name the signal you used, not the announcement you sent.

Approach

  1. Establish the reader set empirically rather than from a wiki of owners: per-field usage counters keyed by principal, or access logs attributed to a consumer. State the blind spot of whichever you pick, since a consumer that reads the field only on a monthly job will not appear in a week of logs.
  2. Ship additive first. Populate the new field alongside the old one so no reader is forced to move, which is also what keeps a rolling deploy safe, because old and new instances answer the same requests at the same time and a rollback must still find the old shape present.
  3. Set the window from the slowest legitimate consumer's release cadence, not from your calendar, and decide separately what to do for a consumer with no release process at all, such as an external webhook endpoint you can only email.
  4. Convert silence into evidence before you rely on it: a short, low-traffic removal window that makes a still-dependent consumer fail visibly and loudly while you are watching, rather than at three in the morning after you have moved on.

Follow-up

  • How would you detect a consumer that reads the field only during a monthly export?
  • One caller refuses to move and has a commercial relationship behind it. What changes in your plan and what does not?

Turn a code review disagreement into a decision

Easy · code review · optimistic concurrency · communication

A colleague's change updates a row with UPDATE resource SET version = version + 1 WHERE resource_id = $1 AND version = $2 and treats an affected-row count of zero as a successful no-op. You read that as a silently lost update; they think returning 200 is friendlier to clients than returning a conflict. Describe how you have handled a review disagreement of this shape: what goes in the comment, when you leave the thread, and who decides. Then write the comment you would leave here, in under 80 words.

Approach

  1. Sort the disagreement before writing anything. A silently discarded write is a correctness claim about data; the choice between 409 and 412 is taste. Only the first justifies blocking a merge, and saying which one you are doing is most of the value of the comment.
  2. Make the claim reproducible in the comment itself with an interleaving rather than a principle: A reads version 7, B reads version 7, B commits version 8, A's predicate matches zero rows, A is told it succeeded and A's edit is gone.
  3. Offer the alternative with its cost attached: return 409 carrying the current version and the revision that won, so the client can re-read and re-apply. Note that automatic retry is not the fix, because a retry re-reads the winner's state and reapplies an intent formed against data that no longer exists.
  4. Apply an escalation rule you can state: two round trips on the thread, then a call, and the service's owner decides rather than the reviewer. A reviewer who cannot be overruled is a bottleneck with extra steps.

Follow-up

  • Where would you put the test that fails if someone reintroduces the swallowed zero rowcount?
  • The author says clients cannot handle a 409. How do you check whether that is true?