Interview guides · Axiom Software Solutions
Axiom Software Solutions Software Engineer Interview Guide
Axiom Software Solutions Software Engineer interview guide: ASP.NET, T-SQL, LINQ, SSRS and validation questions, a 7-day plan and story prep.
The 60-second brief
Candidates describe Axiom Software Solutions as a company that delivers enterprise applications to clients. The Software Engineer role is described as building, maintaining and scaling those systems, covering web applications and database work. The listed stack is .NET, ASP.NET, T-SQL and Visual Studio, with HTML, CSS and JavaScript on the front end. LINQ, SSRS, jQuery and cloud deployment are listed as nice-to-have skills. Day to day, candidates describe writing and debugging code, tuning existing applications, refactoring older code, joining code reviews, writing technical documentation, and syncing with product managers and senior engineers.
This guide is for engineers preparing for the Software Engineer loop at Axiom Software Solutions, especially those with a Microsoft-stack background. It covers the five questions candidates report (ASP.NET experience, relational query tuning, LINQ and SSRS in production, client-side versus server-side validation, and debugging in Visual Studio), the behavioral themes of adaptability and ownership, and a seven-day plan that ends with a coding rehearsal in your own environment.
Candidates describe an initial phone screen, technical assessments that range from theory to hands-on coding, and final interviews with management. No question-to-round mapping is reported, so prepare every reported question for every stage.
Focus areas
- .NET
- ASP.NET
- SQL / Relational Databases
Overview
The reported questions all come from one stack: ASP.NET for the web tier, a relational database queried with T-SQL, LINQ for data access in code, SSRS for reporting, and Visual Studio as the daily tool. That makes this a depth loop rather than a breadth loop. Two of the five questions ask about your own experience, so a prepared story with real decisions in it carries more weight than a recited feature list.
Candidates also report that the technical stages run from theory to hands-on coding, and that you may be asked to code in an environment where you set up your own tools. Preparation therefore has two halves. Be able to explain why behind each choice (client versus server validation, an index versus a rewrite, deferred versus immediate query execution), and be able to write working C# and T-SQL without leaning on a setup you have not rehearsed.
The behavioral themes candidates describe are adaptability to changing requirements, ownership of a project from concept to deployment, and commitment to your longer-term goals and interest in the domain. The role description also mentions explaining technical trade-offs to non-technical colleagues, so prepare at least one story where you did that. Candidates report a lengthy process with several touchpoints, so keep your examples consistent from the first call to the final management interviews.
Where to focus your preparation
Candidates report a phone screen, technical assessments and final management interviews, but no fixed order or timings, so this guide treats the loop as one preparation block and tells you what to confirm with your recruiter.
Preparation focus
Candidates describe a phone screen, then technical assessments that range from theory to hands-on coding, then behavioral interviews with senior leadership, with several people involved and a process that can run long. The reported questions are about ASP.NET, relational query performance, LINQ and SSRS, validation, and debugging in Visual Studio. Because the order and the format of each stage are not reported, ask your recruiter whether you will write code live, in which tools, and on whose machine, then rehearse the reported questions in both spoken and written form. Keep a short checklist for your own environment: a working solution, a SQL client against a sample database, and a browser with developer tools.
What to demonstrate
- Working command of .NET, ASP.NET and T-SQL, including the reasoning behind your choices
- Ability to write clean, working code when the stage is hands-on
- Clear explanation of technical trade-offs to a non-technical listener
- Ownership and adaptability, shown through your own project stories
How to prepare
- Write a one-page inventory of each project that used ASP.NET, T-SQL, LINQ or SSRS, with your own contribution, the version you used and one measurable result
- Rehearse the reported theory questions aloud: query tuning, client versus server validation, and your Visual Studio debugging routine
- Set up and test your own coding environment, including a sample database, so a hands-on stage does not start with installation
- Ask the recruiter which stages are coding, which are conversation, and who the final interviewers are
Where candidates lose points
Describing ASP.NET experience as a list of features with no named version, project type or personal decision
Prepare one project with the flavor (Web Forms, MVC, Web API or ASP.NET Core), the version, the part you owned, one problem you fixed and its measured result. Do not claim a flavor you have not shipped.
Answering the query-optimization question with only 'add an index'
Walk a sequence: read the execution plan, find scans and key lookups, check statistics and predicates that block index use (functions on columns, implicit conversions), then choose between an index change, a rewrite and fewer columns or rows. Say how you would measure before and after.
Treating validation as a client-side matter, or calling the two sides interchangeable
State that client-side checks are for fast feedback and can be bypassed, so server-side validation is the enforced one, and that the database constraints are the last line. Be ready to name how you keep the two rule sets from drifting apart.
Reciting LINQ syntax without explaining deferred execution, IQueryable versus IEnumerable, or the SQL it produces
Practice explaining one query that went wrong (a premature ToList, an N+1 from lazy loading) and show how you looked at the generated SQL. Prepare the same kind of incident for an SSRS report.
Going silent when stuck during a hands-on stage, or losing time because your tools are not ready
Narrate what you tried and what you will try next. Because candidates report being asked to set up their own tools, test your solution, SQL client and a sample database ahead of time, so you can start coding immediately.
Questions & practice
Canonicalise a request body into a stable idempotency fingerprint
Medium · parsing · canonicalisation · hashing
idempotency_key.request_fingerprint is a SHA-256 over the method, path and canonicalised body, and a retry whose fingerprint differs must be rejected with 422 rather than served the stored response. Write the canonicaliser. Bodies are JSON up to 256 KB nested at most 32 levels; clients vary key order, whitespace and unicode escaping, and some send 64-bit ids as JSON numbers. Produce a deterministic byte string such that semantically identical bodies match and any semantic difference does not. State your complexity and name two normalisations you refuse to perform.
Approach
- Parse once into a tree, then re-serialise under fixed rules: object keys sorted, array order preserved, one escaping convention, no insignificant whitespace. Parsing is O(n) and sorting keys is O(k log k) per object, so O(n log n) overall with O(depth) stack, and the 32-level cap is enforced during parsing because hostile nesting is how a canonicaliser becomes a stack overflow.
- Sort keys by their UTF-8 bytes and say why the obvious implementation is wrong in some runtimes: a default string comparison that orders by UTF-16 code units places surrogate pairs, meaning code points from U+10000 up, below U+E000 to U+FFFF, which is not UTF-8 byte order, so two services written in different languages disagree on the same document.
- Do not re-encode numbers through a double. IEEE-754 binary64 represents integers exactly only up to 2^53, so normalising a 19-digit id through a float changes it, and 1 against 1.0 cannot be reconciled without deciding whether they are the same value. Preserve the literal token, and require ids as strings at the API boundary if you want them comparable.
- Reject duplicate keys rather than picking one. JSON permits them and parsers disagree, most keeping the last, so any choice you make ties the fingerprint to a parser detail that the code handling the request does not necessarily share.
Follow-up
- A client sends the same logical request with an extra field your API ignores. Same key, different fingerprint, so you return 422. Is that the right answer?
- Where does the fingerprint get computed relative to request decompression and the body-size limit?
Track a rolling failure rate per destination for circuit decisions
Easy · sliding window · ring buffer · circuit breaker
The egress service delivers about 1,500 webhooks per second across roughly 40,000 destinations, each call bounded by a 10 second timeout. Maintain, per destination, the failure rate over the trailing 60 seconds so a caller can ask before dispatch whether the circuit should open. Attempts arrive as (destination_id, finished_at_ms, outcome). Requirement: amortised O(1) per attempt, with total memory bounded by the destination count rather than by traffic. Give the structure, its exact memory, and the rule that stops a destination with three attempts from opening a circuit.
Approach
- Name the exact-deque version and then reject it as the default. Holding timestamps and advancing a tail pointer past anything older than now minus 60 seconds is a correct two-pointer window at amortised O(1) per attempt, but its memory tracks in-window traffic, so one destination in a retry storm holds hundreds of thousands of entries while thousands of quiet destinations hold none.
- Use a ring of 60 one-second buckets per destination, each bucket a pair of counters for attempts and failures. On an attempt, advance the ring by the elapsed whole seconds, zeroing at most min(elapsed, 60) buckets, then increment the head. That is amortised O(1) with a fixed footprint per destination.
- State the footprint: 60 buckets times two 4-byte counters is 480 bytes of payload per destination, so 40,000 destinations is roughly 20 to 25 MB with per-entry overhead, bounded by the catalogue rather than by the rate. The cost is granularity, since the oldest bucket ages out in whole seconds, which is far tighter than the decision needs.
- Require a minimum sample before the circuit may open. A destination with three attempts and three failures reads as 100 percent and is not evidence; a floor of roughly 20 attempts in the window makes the ratio meaningful, and below that floor use a run of consecutive failures as the trigger instead.
Follow-up
- The fleet is 30 instances and each sees roughly a thirtieth of a destination's traffic. Where does the rate actually live, and what does a per-instance answer get wrong?
- A destination answers in 9.5 seconds and succeeds. It is not failing but it is consuming your per-destination concurrency. What signal should open the circuit here?
Identify the heaviest tenants in a five-minute window under memory pressure
Medium · top-k · heavy hitters · streaming
The edge service handles about 3,000 requests per second across roughly 50,000 tenants, peaking near 9,000. Expose the 50 heaviest tenants by request count over the trailing five minutes so limits can be tightened before one tenant's backfill starves the fleet. You may not retain five minutes of raw records. Give the exact solution and its memory, then the bounded-memory approximation with its error stated as a formula, and say which you would ship and at what tenant cardinality that choice changes.
Approach
- Do the exact version first, because it is affordable at this cardinality: a ring of 300 one-second counters per tenant, advanced lazily, is 1,200 bytes of counters per tenant and roughly 60 to 90 MB for 50,000 tenants with overhead. Carry a running total and subtract the bucket you overwrite so a window read is O(1) rather than 300 adds.
- Extract the top 50 with a size-k min-heap over the tenant sums: O(d log k) for d tenants, against O(d log d) to sort them all. Maintaining the heap continuously instead of on query requires a tenant-to-heap-index map, because incrementing a count already inside the heap means sifting from a known position, and without that map you rebuild the heap on every request.
- State the approximation precisely rather than gesturing at sketches. Misra-Gries with m counters retains every item whose true count exceeds N/(m+1), and each retained count underestimates the truth by at most N/(m+1). With m = 1,000 and N = 900,000 requests in the window the error is roughly 900 requests, which is fine for spotting a tenant sending 50,000 and useless for ranking two tenants 200 apart.
- Say what breaks when the window slides: Misra-Gries and Space-Saving are insert-only and cannot be decremented as records age out. The workable construction is one summary per sub-window, say ten seconds, with 30 summaries merged at query time, and the merged error is the sum of the per-summary errors, so the bound degrades linearly in the number of sub-windows.
Follow-up
- The heaviest tenant is heavy because of one export job rather than user traffic. Should the limiter treat those as the same tenant?
- Two tenants sit tied at the boundary of the top 50. Does your answer flap, and does the flapping matter?
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?
Hold a per-tenant active cap against concurrent creates
Hard · write skew · isolation levels · constraints
A tenant on the standard plan may hold at most 50 resources with status='active'. The create handler runs SELECT count(*) FROM resource WHERE tenant_id = $1 AND status = 'active', compares to 50, then inserts. Two creates arrive 3 ms apart on different instances and the tenant lands at 51. Name the anomaly, say whether PostgreSQL 16 READ COMMITTED or REPEATABLE READ prevents it and why, then give an implementation that holds the cap at READ COMMITTED with the exact statements. Finally, say what changes when the cap is 'at most one running export per tenant' on job_run.
Approach
- Name it: write skew. The two transactions read an overlapping set and write disjoint rows, so there is no row-level conflict for the engine to detect and each commit is individually legal.
- Rule out the levels precisely. READ COMMITTED takes a fresh snapshot per statement and takes no lock on the counted rows, so both see 49. PostgreSQL's REPEATABLE READ is snapshot isolation: it removes non-repeatable reads and phantoms within the snapshot but still admits write skew, because the anomaly is not a re-read of a changed row, it is a read of a set that a concurrent transaction invalidates. Only SERIALIZABLE closes it, by tracking the read dependency and aborting one transaction with SQLSTATE 40001 — a guarantee that exists only if the application re-runs the whole transaction from the read.
- Convert the set predicate into a single-row conflict: keep tenant.active_resource_count and run UPDATE tenant SET active_resource_count = active_resource_count + 1 WHERE tenant_id = $1 AND active_resource_count < 50 in the same transaction as the INSERT. Zero affected rows is the cap, returned as 409. The row lock serialises the decision at any isolation level, and contention is bounded to one tenant's row — which is also the fair-scheduling unit, unlike a global counter that would convoy every tenant behind one row.
- State the cost you just took on: a counter is a second source of truth that can drift, so every path that changes status must adjust it inside the same transaction, and a periodic reconciliation has to exist, with resource_revision as the authority for what the count should have been.
Follow-up
- A resource moves from archived back to active. Which statements change, and what breaks if the counter update and the status change land in different transactions?
- The cap becomes plan-dependent and a plan can change mid-month. Where does the number 50 live, and who reads it?
How do you optimize relational database queries for performance?
Medium · Technical Proficiency and Stack Knowledg
Approach
- Measure before changing anything: turn on SET STATISTICS IO, TIME ON and capture the actual execution plan in SSMS, then find the operator doing the most logical reads and compare estimated with actual row counts. For a regression with no obvious cause, Query Store shows when the plan changed.
- Make predicates sargable so an index seek is possible. Replace WHERE YEAR(OrderDate) = 2024 with a range on OrderDate, and remove implicit conversions such as an NVARCHAR parameter compared with a VARCHAR column. Then index for the query: equality columns first, then the range column, with INCLUDE columns to cover it and remove Key Lookups.
- State the cost of indexing: every index slows INSERT, UPDATE and DELETE and takes storage, so index the hot read paths and drop unused indexes (sys.dm_db_index_usage_stats shows them). Avoid SELECT *, which defeats covering indexes.
- Change the query shape where it matters. Replace cursors and row-by-row scalar UDFs with set-based joins or window functions. Use keyset pagination (WHERE Id > @lastId ORDER BY Id) instead of a large OFFSET, which still reads every row it skips. Parameterise with sp_executesql or ADO.NET parameters so plans are reused and injection is blocked.
- Cover what the plan alone misses. Stale statistics give bad cardinality estimates, so run UPDATE STATISTICS. Parameter sniffing caches a plan built for one unusual value; fixes include OPTION (RECOMPILE) or OPTIMIZE FOR. Check for N+1 query patterns from LINQ lazy loading, and use wait types in sys.dm_exec_requests to tell blocking from CPU cost.
Follow-up
- A query runs fast in SSMS but slowly when the application calls it. What would explain that, and how would you confirm it?
- How do you choose the column order of a composite index, and when would you decide not to add an index at all?
- How would you find the most expensive queries on a production server before anyone complains about them?
Explain the difference between client-side and server-side validation in web development.
Medium · Technical Proficiency and Stack Knowledg
Approach
- Define both sides. Client-side validation runs in the browser through HTML5 attributes (required, maxlength, pattern, type=email) or JavaScript, and its job is fast feedback without a round trip. Server-side validation runs in the request pipeline before anything is persisted, and it is the only check you can trust.
- Explain why the server must always re-validate: anyone can disable JavaScript, edit the DOM or send the request straight to the endpoint with curl or Postman. Client checks are a usability feature, not a security control. In ASP.NET MVC or Core, check ModelState.IsValid; with [ApiController], an invalid model returns a 400 with ValidationProblemDetails automatically. In Web Forms, check Page.IsValid.
- Keep one source of rules to prevent drift. Data annotations on the view model ([Required], [StringLength], [Range], [RegularExpression]) drive both the server check and the data-val-* attributes that jQuery Unobtrusive Validation enforces in the browser. Rules that need data, such as a unique email, run only on the server. [Remote] can preview them over AJAX, but the submit must check again.
- Separate validation from injection defence. Validation does not stop SQL injection, so use parameterised queries. XSS is prevented by output encoding, which Razor applies by default. Back the rules with database constraints (UNIQUE, CHECK, foreign keys) to catch race conditions that application checks miss.
- Name the edge cases: date and decimal formats that depend on culture and parse differently in JavaScript and .NET, regex syntax that differs between the two engines, whitespace trimming, and string length limits that must match the column sizes. On failure, return errors per field and keep the user's input in the form.
Follow-up
- Someone skips the form and POSTs malformed data directly to the endpoint. Walk through what stops it at each layer.
- How would you enforce that an email address is not already registered if two people submit the same address at the same moment?
- Where would you put a rule that depends on two fields, such as an end date that must come after a start date, and how would it run in the browser too?
How do you approach debugging complex issues within Visual Studio?
Medium · Technical Proficiency and Stack Knowledg
Approach
- Start with a reliable repro: the exact input, environment and full stack trace, including every InnerException. In Exception Settings (Ctrl+Alt+E), set the debugger to break when an exception is thrown, so it stops at the throw site rather than at a catch block that swallows or rewraps the exception.
- Use the right breakpoint for the job. Conditional breakpoints (a condition or hit count) skip iterations you don't care about. Tracepoints log a message without stopping execution. Data breakpoints (for .NET Core 3.0 and later) stop when a field's value changes. Inspect state in Call Stack, Locals, Autos and Watch, and test hypotheses in the Immediate Window.
- Match the tool to the type of bug. For async or multithreaded bugs, use the Threads, Tasks and Parallel Stacks windows, and remember that races can vanish when a breakpoint changes timing, so log instead. For leaks and slowness, compare memory snapshots and profile CPU in the Diagnostic Tools window. For production-only bugs, attach to w3wp.exe for IIS-hosted ASP.NET or open a .dmp file, with PDBs that match the build.
- For data-layer bugs, capture the SQL that was actually sent (EF Core logging or ToQueryString, SQL Server Profiler or Extended Events) and run it in SSMS with the same parameter values. Debug the JavaScript side in browser dev tools, so a client bug is not mistaken for a server bug.
- Close the loop. Confirm the root cause explains every symptom, not only the first one. Write a failing unit test that reproduces the bug before fixing it. If it is a regression, git bisect between a known-good and a known-bad commit finds the change that introduced it.
Follow-up
- A breakpoint shows as hollow with 'will not currently be hit'. What are the likely causes and how do you check each one?
- The failure happens only in production and you cannot attach a debugger. What do you collect and how do you analyse it?
- How would you debug an intermittent failure that disappears whenever you step through the code?
Suggested practice checklist
The plan follows the five reported questions and the listed stack: format and inventory first, then ASP.NET and validation, T-SQL tuning, LINQ and SSRS, debugging, stories, and a final coding rehearsal. Practice-library questions are linked where they train a related skill.
Day 1 — Confirm the format and inventory your experience
- Email your recruiter to ask which stages involve live coding, which tools and environment you are expected to bring, and who the final interviewers are.
- List every project where you used ASP.NET, T-SQL, LINQ or SSRS, with the version, your own part and one outcome per project.
Deliverable: A written reply from your recruiter about the format and a one-page project inventory with version, role and outcome for each project.
Day 2 — ASP.NET and web fundamentals
- Write out the request path of an ASP.NET app you know (routing, controller or page, data access, response) and note where dependency injection and authentication enter.
- Prepare an answer to the client-side versus server-side validation question: what each side does, why server-side is mandatory, and how you share rules between them.
- Build a small form with a client-side check and a server-side check, then bypass the client check in browser developer tools to confirm the server rejects it.
Deliverable: A one-page request-path diagram, a spoken validation answer, and a working form whose server-side check rejects input that bypassed the client check.
Day 3 — T-SQL and relational query tuning
- Take a slow query on a sample database, capture its execution plan, then fix it with an index change and a rewrite, and record before and after reads.
- Write your spoken answer to the query-optimization question as an ordered sequence: plan, scans versus seeks, statistics, sargable predicates, fewer rows and columns.
- Work the soft-delete re-registration and per-tenant cap practice questions to rehearse constraints and concurrency in SQL.
Deliverable: A before and after execution-plan comparison for one query, a written optimization checklist, and solutions to the two practice SQL questions.
Day 4 — LINQ and SSRS in production
- Write one LINQ query three ways (premature ToList, correct IQueryable filter, projection with Select) and compare the SQL each generates.
- Reproduce an N+1 query with lazy loading and fix it with Include, then confirm the query count.
- Write the two-part LINQ and SSRS answer, each half anchored by one real incident and its measured fix.
Deliverable: Three generated-SQL comparisons for the LINQ query, a before and after query count for the N+1 fix, and a written two-part answer with one incident each.
Day 5 — Debugging in Visual Studio
- List your debugging sequence: reproduce, read the exception and stack trace, set breakpoints and conditional breakpoints, inspect locals and watch windows, and check logs and the SQL a query produced.
- Debug a deliberately broken sample application using breakpoints, the Immediate window and exception settings, and note which technique found each defect.
- Rehearse the spoken answer using one real hard bug from your past, naming the cause and how you proved it.
Deliverable: A written debugging checklist, notes on which Visual Studio tool found each defect in the sample application, and a spoken answer built on one real bug.
Day 6 — Stories for the behavioral stages
- Write the ASP.NET experience story in the structure context, your decision, a problem, a measured result, what you would change.
- Prepare stories for adaptability (a requirement that changed mid-project), ownership (concept to deployment) and explaining a technical trade-off to a non-technical person.
- Write your answer on your long-term goals and what draws you to this domain, then rehearse the outage practice story aloud.
Deliverable: Four written stories (ASP.NET project, adaptability, ownership, trade-off explanation), a goals answer, and one recorded spoken run of the outage story.
Day 7 — Coding rehearsal in your own environment
- Open your own environment from scratch, as if starting a hands-on stage, and confirm the solution builds, the SQL client connects and a test project runs.
- Work the idempotency-fingerprint practice question in C# in that environment, narrating your reasoning aloud.
- Run through one spoken pass of all five reported questions, then write the questions you will ask the hiring manager.
Deliverable: A working environment checklist, a completed C# solution for the practice question, a spoken pass of the five reported questions, and a list of questions to ask.
Behavioural preparation
The behavioral themes candidates describe are adaptability to changing requirements, ownership of work from concept to deployment, and commitment to your longer-term goals and the domain. The two reported experience questions about ASP.NET and about LINQ and SSRS are best answered as stories with decisions and outcomes, so rehearse them with the same discipline as the prompts below.
- Tell me about a time requirements changed in the middle of a project. What did you change, and what did it cost?
- Walk me through a project you owned from first concept to deployment.
- Describe a time you explained a technical trade-off to someone who was not an engineer.
- Where do you want your career to go, and what interests you about this domain?
Describe your experience with ASP.NET and how you have utilized it in past projects.
Medium · Technical Proficiency and Stack Knowledg
Approach
- Pick one ASP.NET project you can describe end to end and name exactly what you used: Web Forms, MVC, Web API, Razor Pages or ASP.NET Core, and the .NET version. A precise account of one real stack beats a survey of the framework, and claiming a flavor you have not shipped is easy to expose.
- Give the context in two sentences, then your own responsibility. Spend the rest on three decisions with reasons: how authentication and session or state were handled, where validation lived, and how data access was layered (Entity Framework or ADO.NET, stored procedures, dependency injection registration).
- Include one problem you found and fixed, such as a slow page, heavy ViewState or session memory, or an N+1 query behind a list view. Say how you located it (profiler, logging, execution plan) and give a real before and after number such as response time or error count. If you have no number, describe the observable change instead.
- Finish with deployment and what you would change now, for example moving legacy pages to ASP.NET Core or adding automated tests. The story should show that you owned the decision, not that you watched a team make it.
Follow-up
- Why did you pick that ASP.NET approach over the alternatives you had available?
- How did you handle authentication, authorization and input handling in that application?
- If you had to migrate it to ASP.NET Core today, what would you change first?
What is your experience with LINQ and SSRS in a production environment?
Medium · Technical Proficiency and Stack Knowledg
Approach
- Answer in two parts, LINQ then SSRS, each anchored in a production situation. If you have used only one of them, say so plainly, then describe how you would verify the other rather than bluffing.
- LINQ: separate LINQ to Objects from LINQ to Entities. IQueryable builds an expression tree that is translated to SQL and runs when you enumerate it, so calling ToList before Where pulls the whole table into memory. Cover deferred execution, projecting with Select instead of loading full entities, Include or explicit loading to fix N+1 queries, AsNoTracking for read-only paths, and inspecting the generated SQL through logging or a profiler.
- SSRS: describe reports you built or maintained: parameters, datasets backed by stored procedures, shared data sources, deploying RDL files to the report server, and scheduled subscriptions. For performance, push filtering and aggregation into the dataset query instead of the report layout, avoid subreports that query once per row, and consider caching or snapshots for heavy reports.
- Tie each half to a production incident, such as wrong totals from a join that duplicated rows, a report that timed out under real data volume, or a LINQ query that returned thousands of rows. State how you diagnosed it, what you changed and how you confirmed the fix. Listing features without an incident is the common weak answer.
Follow-up
- What is the difference between IEnumerable and IQueryable, and at what point does the SQL actually run?
- A report that ran quickly in testing is slow in production. How do you investigate it?
- How did you deploy and version report or query changes without breaking existing users?
Narrate an outage you owned from page to postmortem
Hard · incident response · blast radius · postmortems
Pick an incident you personally drove, ideally one where writes were affected rather than reads. In six to eight minutes: state the symptom as it first appeared on a dashboard, the blast radius you established before you knew the cause, the mitigation you applied and when, the mechanism you eventually proved, and the follow-up that would prevent a repeat. Bring numbers: error rate, tenants affected, minutes to mitigate, minutes to resolve. If you cannot name what you measured, choose a different incident.
Approach
- Open on the signal rather than the cause: which metric at which percentile moved, on which service, at what time, so the listener follows the same evidence you had rather than a conclusion you already reached.
- Separate mitigation from diagnosis out loud. State what you did to stop the bleeding (flag off, shed traffic, drain a lease, roll back a deploy) and say plainly that you did it before the mechanism was known, because those are two jobs with different deadlines.
- Establish blast radius in countable terms: how many tenants, how many writes, and crucially whether the effect was loss or only delay. An append-only revision table or a pending outbox row means the change survived and the projection was merely behind, which is a repair rather than a data-loss incident.
- Prove the mechanism instead of asserting it. Name the trace span that grew, the plan that flipped to a sequential scan, the lease that expired, plus one alternative you ruled out and the signal that stayed flat while you ruled it out.
Follow-up
- What would you do differently in the first five minutes, given the same dashboard and no more information?
- Which follow-up action did you deliberately not take, and why was dropping it the right call?