Interview guides · Allstem Connections
Allstem Connections Software Engineer Interview Guide
Allstem Connections Software Engineer interview guide: reported questions on hardware-aware optimization, debugging and design, plus a 7-day plan.
The 60-second brief
Candidates describe the Software Engineer role at Allstem Connections as software work that sits between electrical engineering requirements and the applications built on top of them. The areas mentioned are remote automotive electronics and industrial adhesives, with clients as the users of what gets delivered. Day-to-day work covers design and prototyping, testing, deployment, code review and system documentation, done alongside electrical and systems engineers.
This guide is for candidates interviewing for the Software Engineer position at Allstem Connections. It covers the four questions candidates report (a story about hardware constraints, maintainability in distributed systems, debugging across software and hardware, and language or framework trade-offs), the topic areas reported for the loop, and the stack listed for the role: C++, Python or embedded languages, Git, and optionally AWS or Azure and CAN bus. No company-specific coding exercise is reported, so none is assumed.
No named rounds or order are reported. Candidates describe a process that usually begins with a recruiter screen, moves to technical discussions with engineering leads, and runs roughly two to four weeks.
Focus areas
- Electrical Engineering Fundamentals
- Electronics Engineering
- Software Engineering (General)
Overview
The reported questions share one theme: reasoning where software meets physical limits. One asks for a story about tuning software to fit hardware constraints. One asks how you debug a problem that crosses a software and hardware interface. Two are broader engineering questions, on keeping large distributed systems maintainable and on weighing languages and frameworks for a new project. Preparing well means having a concrete, numbers-backed story for the first, a repeatable method for the second, and a criteria-based answer for the others.
The topic areas candidates report are Electrical Engineering Fundamentals, Electronics Engineering, Software Engineering (General), Building Automation Systems (BAS) and Control Systems Engineering. Two design scenarios are also reported: a data logging system for a remote sensor array, and a system that handles real-time data updates with minimal latency. If your background is pure software, spend time on the electrical side: voltage and current basics, pull-up resistors, sampling and noise, how a feedback control loop works, what a bus such as CAN carries, and how building automation protocols such as BACnet and Modbus expose points. If your background is embedded, rehearse the distributed-systems side: retries, idempotency, ordering and back-pressure.
Candidates also report that the role is remote, with daily stand-ups and regular syncs, and that explaining trade-offs to non-technical stakeholders matters. Practise saying the reason behind each technical choice in plain language, and be ready to describe the hardest technical problem you solved in the last year, including what went wrong along the way.
Where to focus your preparation
Candidates do not report named rounds, so the loop below is one preparation stage covering everything reported; confirm the actual stages with your recruiter.
Preparation focus
Candidates report a process that usually starts with a recruiter screen and continues with technical discussions with engineering leads, over roughly two to four weeks. No stage names, order or coding format are reported, so treat this as one preparation block and ask the recruiter what each stage will contain and whether you will write code. Approach it by covering three layers: a hardware-constraint story you can tell with numbers, a spoken method for debugging across a software and hardware interface, and design reasoning for the two reported scenarios (a sensor-array data logger and low-latency real-time updates).
What to demonstrate
- Candidates report topic areas spanning electrical fundamentals, electronics, general software engineering, building automation and control systems
- Candidates report questions on optimizing software for hardware constraints and on debugging across software and hardware interfaces
- Candidates report advice to explain the reason behind a technical choice, not only how it was done
How to prepare
- Write a one-page story for the hardware-constraint optimization question with baseline and final measurements, then rehearse it aloud
- Refresh basics a software engineer can be asked about on the electrical side: Ohm's law, pull-up resistors, ADC sampling, noise, what a CAN bus frame carries, and how BACnet and Modbus expose sensor points
- Outline designs for the sensor-array data logger and the low-latency real-time update scenario, covering buffering, loss on a flaky link, and ordering
- Ask your recruiter for the stage list, who you will speak with, and whether a coding exercise is included
Where candidates lose points
Telling the hardware-constraint story without a baseline or a final number
Write down the constraint, the measurement before, the measurement after, and the tool you used. A result like 'much faster' cannot be checked; a before and after figure on the same workload can.
Answering the maintainability question with only 'clean code and tests'
Name distributed-system specifics: versioned interfaces between services, contract tests, structured logs and tracing, small modules with clear ownership, documented failure behavior, and code review habits. Add the device side: older firmware still sending older message formats. Tie each to a failure it prevents.
Debugging a software and hardware problem by guessing a cause in the firmware or the cloud
Describe a sequence: reproduce, find what changed, isolate the interface with a scope, bus trace or logs on both sides, swap one component at a time, and separate the trigger from the cause before you write the fix.
Choosing a language or framework by personal preference
List the criteria first: real-time or memory limits, available libraries and drivers for the target hardware, team skills, tooling and testing, and long-term support. Then show how the criteria pick C++, Python or an embedded language for a specific case.
Treating the role as pure software and skipping the electrical, control and building automation topics candidates report
Give one session each to electrical and electronics basics, to control loops and CAN bus, and to building automation: BACnet and Modbus point reads (objects, registers, polling versus change-of-value) and HVAC control points such as setpoints, schedules and alarms. Aim to discuss sensors, sampling and feedback without hesitation.
Questions & practice
Diff a projection against the primary without per-row point reads
Hard · reconciliation · range hashing · throttling
The listing projection has drifted and some rows show a stale version. The primary holds 40,000,000 resource rows across 12,000 tenants while serving 1,200 writes and 14,000 reads per second. The obvious repair, reading each resource row and comparing its version against the projection, is correct and would eventually finish. Explain precisely why it is unacceptable here, then give a diff that finds the differing rows, state its complexity, and make it safe to run against a live primary. Replication lag is usually under 100 ms and is not bounded.
Approach
- Quantify the naive cost rather than calling it slow: 40,000,000 point reads at even 0.5 ms each is over five hours serialised, and the only lever is concurrency, which is exactly what you cannot spend. The primary's pool is sized for the write path, and 40,000,000 random reads evict the buffer cache that sustains the 85 percent cache hit rate, so the audit degrades the system it is auditing.
- Replace random access with one ordered pass per side. Both sides can be read in (tenant_id, resource_id) order, which is a sequential scan on each and a merge join in O(n) time and O(1) memory. For a dense diff that is the whole answer, and it reads the primary once instead of 40,000,000 times.
- For the expected sparse case, compare range hashes instead of rows: partition the key space, compute per range an order-independent aggregate over hash(resource_id, version), compare aggregates, and descend only into ranges that differ. With d differing rows and branching factor B, at most d ranges mismatch per level, so the drill-down examines O(d log_B(n/d)) ranges and reads full rows only in mismatching leaves.
- Aggregate with a sum modulo 2^64 or a multiset hash, never XOR. XOR is order-independent but self-cancelling, so two rows wrong in the same way, or a row duplicated on one side, leave the range aggregate matching and the range is declared clean.
Follow-up
- The diff reports 900 stale rows. How do you decide between patching those rows and rebuilding the projection from resource_revision?
- Same job, but the projection lives in a search index that cannot be scanned in key order. What changes?
Archive a resource graph without breaking live references or recursing
Medium · graph traversal · topological order · tenant isolation
Resources reference other resources within a tenant; for the largest tenant the reference table holds up to 2,000,000 nodes and 8,000,000 edges. Archiving a resource must archive everything reachable from it that nothing outside the set still references, refuse when a live external referrer exists, and terminate when references form cycles, which they legitimately do. Produce the archive order and the refusal list, targeting O(V+E). Say what stops the traversal crossing a tenant boundary, and why recursion is the wrong control structure at this size.
Approach
- Load the subgraph with the tenant predicate on both endpoints of the edge, not only on the side you started from. Scoping the left table alone is the classic cross-tenant leak: one mis-entered edge then pulls another tenant's resources into the traversal and, worse, into the archive.
- Traverse iteratively with an explicit stack. A 2,000,000-node graph can hold a chain deep enough to exhaust a native stack in the low tens of thousands of frames, and that failure is a process crash rather than an error you can return.
- Treat cycles as data rather than corruption: compute strongly connected components with Tarjan in O(V+E) using its own explicit stack, then condense. The condensation is a DAG, so a topological order over it gives the archive order, and every member of a component archives in one transaction because no order within a cycle is valid.
- Decide refusals with reverse edges. A candidate is archivable only if every in-edge originates inside the candidate set, so build the transpose or count in-degrees restricted to the visited set, and emit each blocked resource with the id of the external referrer, which is the only part of the answer an operator can act on.
Follow-up
- The graph is read in one query and the archive writes a minute later. What can change in between, and how do you make the write safe?
- The candidate set is 400,000 resources. Is that one transaction, and if not, what does a half-finished archive look like to a reader?
Merge partitioned event streams into one ordered feed with bounded lateness
Hard · k-way merge · watermarks · out-of-order streams
The read-model service consumes 64 log partitions carrying about 4,000 events per second in total. Each partition is ordered within itself, but partitions drift by up to 30 seconds, and the activity feed must present a tenant's events in occurred_at order. Produce the merge. State its complexity, the buffer it requires in events and in bytes, what happens when one partition is idle, and what you do with an event that arrives after you have already emitted its position. Payloads average 1 KB.
Approach
- Merge with a min-heap over the 64 partition heads keyed on (occurred_at, event_id): O(log P) per event and O(n log P) overall. The tie-break on event_id is what makes the output deterministic when two partitions carry the same millisecond, which matters because the feed is paginated and a non-deterministic order reorders pages under the reader.
- Emitting the heap head is only correct once every partition has produced everything up to that timestamp, so the emit condition is a watermark: the minimum across partitions of the highest occurred_at seen, less the allowed lateness. Events are held until the watermark passes them, which is what turns individually ordered streams into a jointly ordered one.
- Size the buffer from the lateness rather than guessing: 4,000 events per second times 30 seconds is 120,000 buffered events, and at 1 KB each about 120 MB of heap. That number is the real price of the ordering guarantee and belongs in front of whoever asked for it.
- Handle the idle partition explicitly, because it fails the feed rather than corrupting it: a partition with no traffic never advances its own maximum, so the watermark freezes and output stops entirely. Either every partition emits a periodic idle marker carrying the broker's current time, or the watermark falls back to wall clock for a partition silent beyond a threshold.
Follow-up
- The lateness budget is raised to five minutes. What is the new buffer, and what besides memory changes?
- The consumer restarts. Where does it resume from, and what does the feed look like for the first 30 seconds?
Keep soft-deleted accounts from blocking re-registration
Easy · soft delete · unique constraints · engine differences
app_user holds user_id, tenant_id, email CITEXT, password_hash (NULL for SSO principals), email_verified_at, auth_version, status ('invited','active','suspended','deactivated'), created_at, updated_at, deleted_at. Two live accounts for one address inside a tenant must be impossible, but an address freed by a soft delete must be reusable, and the same tenant may delete and re-register it repeatedly. Write the uniqueness DDL for PostgreSQL 16, then the equivalent for MySQL 8 where partial indexes do not exist, and say what each permits once three deleted rows already hold that address.
Approach
- Start from what is actually unique: not (tenant_id, email), but (tenant_id, email) among live rows. PostgreSQL says that directly — CREATE UNIQUE INDEX app_user_live_email ON app_user (tenant_id, email) WHERE deleted_at IS NULL. A full constraint over the same two columns burns the address permanently the first time someone deletes an account.
- Keep case-insensitivity in the type or the index, never in the application: CITEXT as given, or UNIQUE (tenant_id, lower(email)) as an expression index where the extension is unavailable. A case-sensitive unique column is exactly how two accounts for one human appear.
- For MySQL 8 the predicate has to move inside the key: add a discriminator column that is a constant 0 while the row is live and is set to user_id on delete, with UNIQUE (tenant_id, email, deleted_marker). Live rows share the constant and still collide; deleted rows differ from each other and stop colliding.
- State the NULL variant and its dependency: leaving the marker NULL for deleted rows also works, because a unique index treats NULLs as distinct — true in MySQL, and true in PostgreSQL only under the default NULLS DISTINCT, which PostgreSQL 15 lets you reverse. Check the polarity against the three existing deleted rows: constant-on-live is what preserves the collision you want, and reversing it silently admits duplicate live accounts.
Follow-up
- A deleted account re-registers with the same address the next day. Do the old resource rows follow the new user_id, and how does the API keep the two principals apart?
- How do you honour an erasure request while resource_revision.actor_user_id still references this table?
Write the update path that detects a concurrent edit
Medium · optimistic concurrency · transactional outbox · audit history
resource carries version INT NOT NULL DEFAULT 1. resource_revision holds revision_id, resource_id, version, actor_user_id, change_kind, patch JSONB, request_id, created_at with UNIQUE (resource_id, version). outbox_event holds aggregate_type, aggregate_id, aggregate_version, event_type, payload, status. A PUT carries the version the client read. Write the exact statements for the single transaction that applies the edit, records the revision and enqueues 'resource.updated', and give the handler's branch on zero affected rows. Then say what PostgreSQL 16 does under READ COMMITTED when two of these updates hit one row at once.
Approach
- One transaction, three writes, no network call inside it: UPDATE resource SET title = $3, version = version + 1, updated_at = now() WHERE resource_id = $1 AND tenant_id = $4 AND version = $2; then INSERT the resource_revision row at version $2 + 1; then INSERT the outbox_event row at the same aggregate_version. The event goes to a table rather than a broker because no transaction spans both.
- Branch on the affected-row count before doing anything else. Zero has three causes — stale version, wrong tenant, row gone — so re-read once and map to 409 carrying the current version, or 404 for an id outside the caller's tenant, which also stops the endpoint confirming that another tenant's id exists.
- State the engine behaviour instead of assuming it. Under READ COMMITTED the second UPDATE blocks on the row lock, and when the first commits PostgreSQL re-evaluates the WHERE clause against the newly committed row, so the version predicate now fails and the statement reports zero rows. Under REPEATABLE READ the identical collision raises SQLSTATE 40001 instead, so the handler must fold both shapes into one conflict response.
- Keep UNIQUE (resource_id, version) even though the predicate already serialises writers. It is what makes a lost update unwritable if any other path ever reaches the revision table, and it converts a logic bug into 23505 rather than into a silently missing history row.
Follow-up
- A client sends the version it read ten minutes ago and the resource has moved three versions. What is in your 409 so it can resolve the conflict without a full re-fetch?
- Two editors, two disjoint fields, no overlap. Does your answer still refuse the second write, and should it?
How do you ensure code maintainability when working on large-scale, distributed systems?
Medium · Technical and Domain Expertise
Approach
- Start with explicit contracts at every service boundary: versioned schemas (Protobuf, Avro or OpenAPI) and backward-compatible evolution rules. Add only optional fields, never reuse a Protobuf field number, and make readers ignore unknown fields. Contract tests in CI catch a breaking change before it merges.
- Give each service a single owner and its own data, and record each significant design choice in an architecture decision record. Weigh shared code out loud: a shared library removes duplication but ties every consumer to its release cadence, so keep it thin and limited to cross-cutting concerns (logging, retry policy, auth) and accept a little duplicated domain logic rather than coupled deploys.
- Build observability in from the start. Use structured logs carrying a trace or correlation ID that is propagated across calls (for example the W3C traceparent header through OpenTelemetry), plus rate, error and duration metrics per endpoint, so you can follow one request through every hop.
- Make change safe. Keep a test pyramid of many unit tests, contract tests at the boundaries and a few end-to-end tests. Use expand-and-contract migrations for schema changes, feature flags for rollout and idempotent handlers, so a retried message cannot apply twice.
- Cover the device side, since this role works next to hardware. Field devices run mixed firmware versions and some are never upgraded, so the cloud side has to keep accepting older message formats. Put a protocol version in every device-to-cloud message, keep a decoder per supported version tested against recorded payloads, and roll firmware out over the air in staged cohorts with health checks and a rollback path.
Follow-up
- How would you rename a field that five services and a fleet of unupgraded devices read, without a coordinated deploy?
- When would you merge two services back into one, and what signal would tell you to?
- A firmware rollout to the first cohort raises error rates. What do you check, and when do you roll back?
How do you evaluate the trade-offs between different programming languages or frameworks for a new project?
Medium · Technical and Domain Expertise
Approach
- Start from the hard constraints, not preferences. That means the target platform (a microcontroller with kilobytes of RAM, a Linux gateway or a cloud service), any real-time or latency bound, the memory budget, any required coding standard such as MISRA C/C++, and whether the hardware vendor's SDK and toolchain support the language.
- Score the remaining options in a weighted matrix. Criteria are determinism (garbage-collected runtimes such as Java, Go or Python can pause; C, C++ and Rust give predictable timing), the ecosystem for the job (python-can and SocketCAN for host-side CAN tooling, for example), team skill, long-term support and licensing, and the build and deploy story.
- Prototype the riskiest requirement instead of debating it. A short spike that measures the one property that could rule an option out, such as frame-parsing throughput, memory footprint or startup time, settles more than any comparison table.
- Give a concrete split and its cost. For example, use C++ on the device for direct register access and predictable timing, and Python for test harnesses, data analysis and host scripts where speed of development matters more than runtime. Connect them through a defined protocol or pybind11, and accept the extra build tooling and skills that two languages require.
- Write the decision down with the options you rejected, the assumptions behind the choice and the conditions that would make you revisit it. Estimate the exit cost: how much code would have to be rewritten if the choice proves wrong.
Follow-up
- When would you choose Rust over C++ for new firmware, and what would it cost the team?
- The team knows Python well but one component has a hard real-time deadline. How do you handle that?
- A year in, the framework you chose is holding the project back. How do you decide whether to migrate?
What is your process for debugging a complex issue that spans both software and hardware interfaces?
Medium · Technical and Domain Expertise
Approach
- Pin down the failure before forming a theory. Note how often it happens and under what conditions (load, temperature, power state, one unit or every unit), and record the exact firmware, driver and board revision. Establish whether it is deterministic or intermittent, because that decides the tools you use.
- Split the problem at the interface. Capture what actually crosses the boundary with a logic analyzer or oscilloscope (SPI, I2C, UART) or a CAN bus analyzer, then compare it with what the software logged as sent and received. A mismatch shows which side is wrong.
- Isolate each side. Swap in a known-good board, run a loopback test, replace the device with a simulator or stub (hardware-in-the-loop), and run git bisect across firmware versions if the fault is a regression.
- Check the usual cross-boundary causes in order: clock or baud-rate mismatch, endianness and struct packing, races between an interrupt handler and the main loop (a missing volatile or critical section), watchdog resets from missed deadlines, and signal integrity. On CAN, a missing 120-ohm terminator can cause reflections and error frames, especially on longer buses or at higher bit rates; with power off, measure about 60 ohms between CAN_H and CAN_L as the first check. A node goes bus-off once its transmit error counter passes 255.
- Watch for instrumentation that changes timing. A printf inside an interrupt handler can hide a race, so toggle a GPIO and scope it, or write to a ring buffer and dump it later. Finish with a regression test and a written root cause that separates the trigger from the defect.
Follow-up
- The bug disappears when you add logging. What does that tell you, and what do you try next?
- Only some units in the field fail. How do you narrow down whether it is hardware variation or software?
- How would you show that a fix for an intermittent fault actually worked?
Suggested practice checklist
The plan starts by confirming the format, then covers the reported topic areas (electrical, control systems, building automation, general software), the two design scenarios, the debugging and trade-off questions, and finally your own stories and a full mock.
Day 1 — Confirm the format and inventory your stack
- Email your recruiter to ask for the stages, who you will meet and whether you will write code.
- List your hands-on experience with C++, Python or embedded languages, Git workflows, AWS or Azure, and CAN bus, and mark the gaps.
Deliverable: A sent recruiter email and a one-page skills inventory with the gaps marked.
Day 2 — Electrical and electronics refresher
- Review Ohm's law, voltage dividers, pull-up and pull-down resistors, and ADC sampling and noise.
- Write three one-paragraph explanations of these ideas in plain language, as if speaking to a non-technical stakeholder.
Deliverable: Three plain-language explanations of electrical concepts, written out.
Day 3 — Control systems, building automation and CAN bus
- Sketch a PID feedback loop using an air handler's supply-air temperature as the example: setpoint, measured value, valve or damper output, and the schedules and alarms around it. Explain what each PID term adjusts.
- Write a short summary of how a CAN bus frame carries data and why message priority matters.
- Study BACnet (BACnet/IP and MS/TP, objects and properties, change-of-value subscriptions versus polling) and Modbus RTU and TCP (holding versus input registers, polling intervals, scaling raw register values into engineering units).
- Map both protocols onto the sensor-array data logger: choose COV or polling per point, store trends with timestamps and units, and flag stale or out-of-range readings as alarms.
Deliverable: A control-loop diagram, a half-page CAN summary, and a one-page note mapping BACnet and Modbus points into the sensor-logger design.
Day 4 — Design: sensor logger, low latency and maintainability
- Outline a data logging system for a remote sensor array: local buffering, upload retries, idempotent writes, timestamps and gaps.
- Outline a real-time update path with minimal latency and state where you accept staleness.
Deliverable: Two design outlines and a recorded spoken answer on maintainability.
Day 5 — Debugging and technology trade-offs
- Write your debugging sequence for a fault that appears only on real hardware and rehearse it aloud with one real example.
- Build a criteria table for choosing between C++, Python and an embedded language, and test it on two hypothetical projects.
- Answer the language and framework trade-off question aloud.
Deliverable: A debugging checklist and a filled-in criteria table, each used in a spoken answer.
Day 6 — Stories: constraints, failures and trade-offs
- Write the hardware-constraint optimization story with baseline and final numbers.
- Prepare your hardest technical problem from the last year, including a failure and what you changed.
Deliverable: Three written stories, each with a measurable result or a stated lesson.
Day 7 — Full mock and questions to ask
- Run a full mock covering one story, one debugging answer and one design answer, and record it.
- Write three questions for the interviewers about team dynamics and current technical challenges.
- Test your video, audio and screen-sharing setup, since the role is remote.
Deliverable: A recorded mock, a list of three questions, and a checked remote setup.
Stories from hardware-adjacent work
The one behavioral question candidates report asks for a time you optimized software for hardware constraints. Candidates also say to be ready to discuss the hardest technical problem of the last year, your failures as well as your successes, and how you explain trade-offs to non-technical stakeholders. Build stories that show your own decisions, the measurements behind them, and what you learned.
- Describe the hardest technical problem you have solved in the last year.
- Tell me about a failure in your work and what you changed afterwards.
- Describe a time you explained a technical trade-off to a non-technical stakeholder.
- Tell me about a problem you found and fixed before it became an incident.
Describe a time you had to optimize software to meet specific hardware constraints.
Medium · Technical and Domain Expertise
Approach
- Pick a project where you owned the optimization and the constraint was physical or fixed: flash or RAM size, CPU cycles, power budget, bus bandwidth or a hard latency deadline. State the constraint as a number, such as 'the image had to fit in 256 KB' or 'the loop had to finish inside its 10 ms cycle'. If you have no embedded story, use a real fixed limit from server, mobile or edge work and say so plainly.
- Show how you measured before changing anything: a profiler, cycle counter, logic analyzer, heap snapshot or timing trace. Name the baseline, the real bottleneck you found, and any idea you dropped because the measurement ruled it out.
- Describe the change in concrete terms, for example removing heap allocation in a hot path, switching to fixed-point arithmetic, changing a data layout for cache behavior, batching bus transactions, or moving work out of an interrupt handler. Say why you chose it over the alternatives.
- Close with the result on the same workload (before and after numbers), what you gave up (readability, portability, headroom), and how you prevented regression, such as a size or timing check in CI. Tell the story in first person singular and make clear which decisions were yours.
Follow-up
- How did you know that was the real bottleneck and not something else?
- What did the optimization cost you in maintainability, and how did you contain it?
- How would you make sure the improvement did not regress after you moved on?
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
- 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.
- 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.
- 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.
- 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?
Argue against a design, lose, and commit anyway
Medium · disagreement · service boundaries · decision records
Describe a design you argued against and lost. State the failure you predicted as a named mechanism, not a feeling about complexity: two services that would need one transaction, a projection with no rebuild path, a write path with no idempotency key. Say what evidence you brought, what the decision maker weighed instead, and what you did after the decision was made: what you instrumented, what you wrote down, and whether the prediction came true. Five minutes.
Approach
- State the prediction in falsifiable form up front: the mechanism, the condition that triggers it, and the observable outcome. A prediction that cannot be checked also cannot be credited to you later.
- Show the evidence you had at the time and label each piece honestly as measured, analogous, or intuition. Keeping the intuition is fine; disguising it as data is the thing that erodes your standing in the next argument.
- Represent the opposing case at full strength, including the constraint you did not control: a fixed date, a team boundary, or the fact that the decision was cheap to reverse and yours was not.
- Make disagree-and-commit concrete. Name the artefact you left behind so the prediction could be settled without you: the alert and its threshold, the counter on the dashboard, the decision note that recorded the trade-off and the condition that would revisit it.
Follow-up
- What threshold on that alert would have proved you right, and did anyone ever look at it?
- If the same proposal arrived tomorrow with the same deadline, would you argue it the same way?