cswork / libraryOpen practice workspace

Interview guides · Aezion

Aezion Software Engineer Interview Guide

Aezion Software Engineer interview guide: reported rounds, MVC, SOLID and design pattern questions, coding and behavioral prep, plus a 7-day study plan.

The 60-second brief

Candidates describe Aezion as a technology partner that builds customized digital solutions for a range of clients, with the aim of simplifying business processes and scaling their operations. The Software Engineer role is described as designing, building and maintaining enterprise applications, working mostly with web frameworks built on the Model-View-Controller pattern and with modular backend services. The requirements candidates quote name ASP.NET MVC, ASP.NET Core or an equivalent MVC framework, an object-oriented language such as C# or Java, SOLID principles, design patterns, database design and SQL, unit tests and dependency injection frameworks. React, Angular or Vue.js, AWS or Azure, Docker and CI/CD show up as nice-to-have skills. Day to day, candidates report a mix of feature work, refactoring and optimizing existing code, code reviews, and working with product managers, QA engineers and client stakeholders.

This guide is for candidates interviewing for the Software Engineer role at Aezion, from freshers to experienced developers. It covers the three reported rounds, all 19 reported questions (MVC pipeline internals, object-oriented design, coding and debugging, and behavioral prompts), the example scenarios candidates describe, and a seven-day plan. Teams are reported to differ in their mix of frontend, backend and full-stack work, so use it alongside whatever the recruiter tells you about your specific team.

Candidates report three rounds over roughly 3-5 weeks: an Initial Touchpoint with a written assessment, Technical Evaluations on coding and architectural design, and a Behavioral Discussion. This is what candidates describe, not a published process.

Focus areas

  • MVC (Model-View-Controller)
  • SOLID Principles
  • Design Patterns

Overview

The 19 reported questions fall into four groups. Five are about how an MVC application handles a request: the full request lifecycle, routing and custom routes, TempData versus ViewData versus ViewBag, global exception handling, and the four filter types. Five are object-oriented design: SOLID with a refactoring story, dependency injection and loose coupling, abstract class versus interface, a design pattern you have used, and decoupling modules. Five are problem solving: reversing a linked list or detecting a cycle, turning a nested loop into linear time, a palindrome check with minimal space, debugging a production memory leak, and quantitative and logical aptitude questions. The last four are behavioral. Most of the technical set asks you to explain mechanics and justify design choices, so the preparation is mostly precise explanation with small code examples, not memorized puzzles.

The questions use ASP.NET vocabulary: TempData, ViewData and ViewBag, and Action, Result, Exception and Authorization filters, are all ASP.NET MVC constructs. The requirements candidates quote accept C# or Java and an equivalent MVC framework, so if your experience is Spring MVC, build your own mapping before the loop. The dispatcher servlet and handler interceptors play the role of the routing and filter pipeline, flash attributes resemble TempData, and a controller advice covers global exception handling. Say which framework you are answering in at the start of each answer, then stay in it.

Candidates also describe example scenarios worth rehearsing end to end. These are a custom action filter that logs execution time, a notification module for Email, SMS and Push that respects the Open/Closed Principle, a tightly coupled class refactored to use dependency injection so it can be unit tested, the first non-repeating character in a string in a single pass, and a recursive flatten of a deeply nested structure. Each one fits in a short whiteboard or editor session. Practise them with real code and a dry run on small inputs and edge cases.

The first stage can include a written assessment with logic-based multiple-choice questions, in some technical tracks. That part rewards speed on quantitative and logical reasoning, which is a different skill from the framework and design material. Give it its own practice time. The behavioral round centres on changing requirements, motivation for joining, a technical disagreement and competing deadlines, all of which suit a role where you work with client stakeholders. Prepare one real story for each.

The Aezion interview process

Candidates describe three stages in this order, spread over roughly 3-5 weeks. Formats can differ by team and track, so confirm with the recruiter what your first stage looks like.

Initial Touchpoint

Candidates report this first stage as a foundational screening plus a written assessment on core programming and logical aptitude. In some technical tracks the written part includes logic-based multiple-choice questions. Treat it as two separate preparations. For the programming half, refresh the fundamentals of C# or Java: classes and interfaces, collections, strings, exceptions and basic complexity. For the aptitude half, practise quantitative and logical questions with a timer you set yourself, so that reasoning quickly and accurately without an IDE is routine by the time you sit it. This first conversation is also where candidates are encouraged to ask whether the team leans frontend, backend or full-stack.

What to demonstrate

  • Core programming knowledge in the language you list on your resume
  • Quantitative and logical reasoning, as the written assessment covers it
  • Whether your background matches the kind of team you would join

How to prepare

  • Do a set of mixed aptitude problems (percentages, ratios, number series, work-and-time, logical deduction) against a timer you set, and review every miss.
  • Rewrite from memory the basics of your main language: OOP features, collections, string handling and exception handling.
  • Prepare a short account of the web project you know best, including the framework, database and your own part in it.
  • Write two or three questions about the team's mix of frontend, backend and full-stack work.

Technical Evaluations

Candidates describe this stage as deep technical evaluations of coding and architectural design, with interactive, live sessions. The question types reported for the loop are MVC internals (request lifecycle, routing, state, filters, global exception handling), object-oriented design (SOLID, dependency injection, abstract classes versus interfaces, design patterns), and coding and debugging (linked lists, nested loops, palindromes, memory leaks). Prepare each type differently. For framework internals, be able to draw the pipeline from memory and place every filter, route and state mechanism on it. For design questions, practise the sequence of clarifying requirements, naming core entities, and explaining the structure before any code. For coding questions, practise writing clean functions and dry-running them aloud on small and edge inputs.

What to demonstrate

  • Working code for algorithm and data structure problems
  • Understanding of MVC request processing, routing, filters and state handling
  • Ability to apply SOLID principles and design patterns to a business scenario and justify the trade-offs
  • Debugging method for production problems such as memory leaks

How to prepare

  • Draw the MVC pipeline for a POST request from memory, marking where authorization, action, result and exception filters, model binding and view rendering sit.
  • Build the notification module (Email, SMS, Push) so a new channel needs no change to existing classes, then refactor a tightly coupled class to constructor injection and write a unit test with a fake.
  • Code the linked-list reversal, cycle detection, palindrome check, first non-repeating character and recursive flatten without an IDE, and state time and space complexity for each.
  • Write a memory-leak investigation you could narrate: symptoms, memory snapshots or dumps, comparing object counts over time, finding what retains the objects.

Behavioral Discussion

The final stage is reported as a behavioral discussion on soft skills, cultural alignment and your understanding of Aezion's business objectives. The behavioral questions candidates report are about vague or changing requirements, why you want to join, a technical disagreement with a teammate, and managing several tight deadlines. Candidates are advised to study the company's history, client work and business model beforehand, and the role itself involves working with product managers, QA and client stakeholders, so choose stories that show you communicating across those groups. Write each story down with a concrete situation, your own decision, and a result, and say out loud once or twice before the day.

What to demonstrate

  • Communication and teamwork, as the reported soft-skills focus covers
  • How you handle ambiguity and shifting requirements
  • Your interest in the company's business and the fit with your skills

How to prepare

  • Write four stories mapped to the four reported prompts, each with the situation in two sentences, your decision, and a measurable or observable result.
  • Write a short answer on why you are interested in Aezion that cites something from its public material and links it to a specific project of yours.
  • Pick one story where you were wrong or changed your mind, for the technical-disagreement question.
  • List what you would ask the team about its current focus and immediate deliverables.

Where candidates lose points

Describing the MVC request lifecycle as 'the request hits a controller and returns a view'

Memorize the ordered pipeline for the framework you use and place every stage on it: routing selects the controller and action, the controller is created (with its dependencies injected), authorization filters run first, then model binding, action filters around the action, result filters around result execution and view rendering. Exception filters wrap the action stage. Draw it for a POST request and say where a custom filter would plug in.

Mixing up TempData, ViewData and ViewBag

State the facts in order. ViewData is a dictionary that lives for the current request. ViewBag is a dynamic wrapper over the same data, so it lacks compile-time checking. TempData survives to the next request, which makes it the one for post-redirect-get messages, and it is cleared once read unless you keep or peek it. Add that strongly typed view models are usually preferable to either of the first two.

Reciting the five SOLID letters without a refactoring story

Prepare a before-and-after for the Single Responsibility question: a controller or service that validates, queries, calculates and sends email, split into focused classes behind interfaces. Keep the Open/Closed notification module and a Dependency Inversion example ready too, and name the unit test each change made possible.

Name-dropping a design pattern with no alternatives or cost

For each of Repository, Factory, Singleton, Strategy and Observer, prepare the problem it solved in your project, one alternative you rejected and the downside. A hand-written Singleton hides dependencies and hurts testing, so many teams register a single instance in the DI container instead; be ready to explain that difference.

Producing code that works on the happy path but is never dry-run

After writing the linked-list reversal, cycle detection, palindrome check or first-non-repeating-character function, trace it on an empty input, a single element, and two elements. Check that the palindrome version really uses constant extra space with two pointers, and that your reversal saves the next pointer before overwriting it.

Questions & practice

Write a program to reverse a linked list or detect a cycle within it.

Medium · Problem-Solving, Coding & Aptitude

Approach

  1. Reverse iteratively with three pointers: prev = null, curr = head; loop: next = curr.next, curr.next = prev, prev = curr, curr = next; return prev. O(n) time, O(1) extra space.
  2. Mention the recursive version (reverse the rest, then head.next.next = head, head.next = null): also O(n) time, but O(n) call stack, so a very long list can overflow the stack.
  3. Detect a cycle with Floyd's tortoise and hare: slow moves one node, fast moves two; if they meet there is a cycle, and if fast or fast.next becomes null there is none. O(n) time, O(1) space, against O(n) space for a HashSet of visited nodes.
  4. To find where the cycle starts, reset one pointer to head after the meeting and move both one step at a time; they meet at the cycle's first node.
  5. Test the edge cases aloud: empty list, a single node, two nodes, a node pointing to itself, and a cycle that starts at the head.

Follow-up

  • Reverse only the nodes between positions m and n, or reverse in groups of k.
  • How do you find the length of the cycle once you know one exists?
  • Why are the slow and fast pointers guaranteed to meet if there is a cycle?

How would you optimize a nested loop algorithm to run in linear time complexity?

Medium · Problem-Solving, Coding & Aptitude

Approach

  1. Start by naming what the inner loop does for each outer element: usually a search, a count or a sum over the rest of the input. The fix replaces that inner work with an O(1) step.
  2. Search becomes a hash lookup: for two-sum, store each value's index in a dictionary and check whether target minus the current value is already there, giving O(n) expected time and O(n) extra space instead of O(n^2).
  3. If the input is sorted, two pointers from both ends (or a sliding window for subarray problems with non-negative values) give O(n) time and O(1) space; sorting first costs O(n log n).
  4. Repeated range sums become prefix sums (sum of i..j = prefix[j+1] - prefix[i]); next-greater-element style questions use a monotonic stack where each element is pushed and popped once, so the total is O(n).
  5. State the costs and edge cases: hash operations are O(1) only on average, the extra memory may matter, and duplicates or using the same element twice must be handled explicitly.

Follow-up

  • Find the first non-repeating character in a string while reading it only once.
  • Memory is tight and you cannot use a hash map. What do you do instead, and at what time cost?
  • Why is the monotonic stack solution O(n) even though it has a loop inside a loop?

Solve a series of quantitative and logical aptitude questions designed to test your analytical reasoning skill

Medium · Problem-Solving, Coding & Aptitude

Solve a series of quantitative and logical aptitude questions designed to test your analytical reasoning skills.

Approach

  1. Candidates report that some technical tracks start with a written assessment that includes logic-based multiple-choice questions, so practise the standard topics: percentages, ratios, averages, time and work, time-speed-distance, number series, permutations and combinations, basic probability, and logic puzzles such as syllogisms and seating arrangements.
  2. Learn the formulas that save time: work rates add (jobs taking 10 and 15 days alone take 6 days together, since 1/10 + 1/15 = 1/6); average speed over equal distances is 2ab/(a+b), not (a+b)/2; successive percentage changes multiply (+20% then -20% is a 4% net loss).
  3. For counting, decide whether order matters: nPr = n!/(n-r)! when it does and nCr = n!/(r!(n-r)!) when it does not. For probability, count favourable over equally likely outcomes, and use 1 - P(none) for 'at least one' questions.
  4. Use the answer options: estimate, eliminate the ones that fail a units or parity check, and plug options back in when algebra is slow. Skip a question that is taking long and return to it.
  5. For logic puzzles, write the constraints as a table or diagram before reasoning, and check the final arrangement against every stated condition.

Follow-up

  • Two pipes fill a tank in 6 and 8 hours and a drain empties it in 12. How long does the tank take to fill with all three open?
  • What is the probability that two fair dice sum to 7?
  • Continue the series 2, 6, 12, 20, 30 and explain the rule.

Write a function to check if a given string is a palindrome, optimizing for space complexity.

Medium · Problem-Solving, Coding & Aptitude

Approach

  1. Use two pointers: i at the start, j at the end; compare characters and move inward until they cross. O(n) time and O(1) extra space, against O(n) space for building a reversed copy and comparing.
  2. Clarify the rules before coding: case-sensitive or not, and whether spaces and punctuation count. If they are ignored, skip non-alphanumeric characters inside the loop (char.IsLetterOrDigit in C#, Character.isLetterOrDigit in Java) instead of building a cleaned string.
  3. Compare case-insensitively per character (char.ToLowerInvariant) rather than calling ToLower on the whole string: strings are immutable in C# and Java, so ToLower allocates a full copy and breaks the space target.
  4. Edge cases: the empty string and a single character are palindromes; a string with only punctuation is too under the ignore rule; odd and even lengths both terminate when i >= j.
  5. Mention the Unicode limit: indexing a C# or Java string reads UTF-16 code units, so surrogate pairs and combining characters need rune or grapheme-aware handling if the input is not ASCII.

Follow-up

  • Can the string become a palindrome by deleting at most one character? Keep it O(n).
  • Find the longest palindromic substring. What is the complexity of expanding around each centre?
  • Check whether a singly linked list is a palindrome using O(1) extra space.

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

  1. 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.
  2. 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.
  3. 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.
  4. 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?

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

  1. 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.
  2. 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.
  3. 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.
  4. 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?

Explain the complete lifecycle of an HTTP request within an MVC application.

Medium · MVC Framework & Web Architecture

Approach

  1. Walk the request in order for one stack and name it, for example ASP.NET Core: Kestrel receives it, the middleware pipeline runs (exception handling, static files, UseRouting, authentication, authorization), then endpoint routing selects a controller action.
  2. Inside MVC, list the filter pipeline in order: authorization filters, resource filters, model binding and validation into ModelState, action filters (OnActionExecuting, the action, OnActionExecuted), then result filters around execution of the IActionResult.
  3. For a ViewResult, the view engine finds the Razor view by convention (Views/{Controller}/{Action}.cshtml, then Shared), renders it with the model, and the response passes back out through the middleware in reverse order.
  4. If asked about classic ASP.NET MVC 5, map the same steps: UrlRoutingModule matches the route table, MvcRouteHandler hands off to MvcHandler, the controller factory builds the controller through the dependency resolver, and ControllerActionInvoker runs the filters and the action.
  5. Point out where each concern belongs: cross-cutting work for every request goes in middleware, work tied to MVC actions goes in filters, and the controller creation step is where dependency injection supplies constructor arguments.

Follow-up

  • Trace a POST with an anti-forgery token: where is the token validated, and what does the client get if it is missing?
  • What happens when model binding fails, and how does [ApiController] change that behaviour?
  • Where would you add timing for every action, and would that timing include view rendering?

How does routing work in MVC, and how do you configure custom routes?

Medium · MVC Framework & Web Architecture

Approach

  1. Explain conventional routing: a template such as {controller=Home}/{action=Index}/{id?} maps URL segments to route values, with defaults and optional parameters, registered with MapRoute in MVC 5 or MapControllerRoute in ASP.NET Core.
  2. State that conventional routes are matched in the order they are registered, so a specific route such as blog/{year}/{slug} must come before the default catch-all or the default will swallow it.
  3. Show attribute routing for custom URLs: [Route("orders")] on the controller and [HttpGet("{id:int}")] on the action; MVC 5 needs MapMvcAttributeRoutes(), and ASP.NET Core ranks attribute routes by template specificity rather than registration order.
  4. Use constraints to separate overlapping routes: built-in ones like :int, :guid, :minlength(3) or a regex, and a custom IRouteConstraint when the rule needs code.
  5. Mention that routing also generates outbound URLs (Url.Action, Url.RouteUrl, asp-route tag helpers), so a route change also changes every generated link.

Follow-up

  • Two routes match the same URL. How do you find which one won and make the right one win?
  • How would you add an admin area with its own controllers without clashing with the public routes?
  • A route returns 404 in production but works locally. What do you check first?

What is the difference between TempData, ViewData, and ViewBag, and when would you use each?

Medium · MVC Framework & Web Architecture

Approach

  1. ViewData is a ViewDataDictionary with string keys and object values: it lives for the current request only, passes data from controller to view, and needs a cast when read.
  2. ViewBag is a dynamic wrapper over the same ViewData dictionary, so ViewBag.Title and ViewData["Title"] are the same entry; it reads more cleanly but a typo only fails at runtime.
  3. TempData survives one redirect: an item is marked for deletion once read and removed at the end of that request, while Peek() reads without marking and Keep() retains it. MVC 5 stores it in session by default; ASP.NET Core uses a cookie-based provider by default.
  4. Give the use for each: TempData for a Post-Redirect-Get flash message such as 'Order saved'; ViewData or ViewBag only for small extras like a page title; a strongly typed view model for the real page data, since the compiler checks it.
  5. Name the edge cases: TempData objects must be serializable, the cookie provider is limited by cookie size, and session-backed TempData in a web farm needs shared session state.

Follow-up

  • What happens to a TempData value that the redirect target never reads?
  • Why would you move data from ViewBag into a view model, and what does that change for testing?
  • How does TempData behave when the app runs on several servers behind a load balancer?

Explain the role of filters (Action, Result, Exception, Authorization) in MVC and how they intercept the reque

Medium · MVC Framework & Web Architecture

Explain the role of filters (Action, Result, Exception, Authorization) in MVC and how they intercept the request pipeline.

Approach

  1. Give the order: authorization filters run first and can short-circuit (for example by returning 401 or 403), then model binding, then action filters around the action, then result filters around result execution. ASP.NET Core adds resource filters, which wrap model binding and the rest of the pipeline.
  2. Explain short-circuiting: setting context.Result in an authorization filter or in OnActionExecuting stops the pipeline, so the action never runs; this is how caching or validation filters return early.
  3. Explain exception filters and their limit: in ASP.NET Core they catch unhandled exceptions from controller creation, model binding, action filters and actions, but not from result execution or resource filters, so global handling usually pairs them with UseExceptionHandler middleware.
  4. Cover scope and ordering: a filter can be registered globally (GlobalFilters or MvcOptions.Filters), per controller or per action, runs outer to inner by scope, and the Order property overrides that.
  5. Use the timing-log example: an action filter that starts a Stopwatch in OnActionExecuting and logs in OnActionExecuted. Note that this does not include view rendering, so measure with a result filter or middleware if that matters. Filters that need services use ServiceFilter or TypeFilter.

Follow-up

  • Write a filter that logs execution time only for selected controller actions. How do you make it receive an ILogger?
  • When would you put this logic in middleware rather than in a filter?
  • A filter registered globally and one on the action both set a result. Which one wins, and why?

Walk me through the SOLID principles and provide a real-world scenario where you refactored code to comply wit

Medium · Object-Oriented Design & SOLID Principle

Walk me through the SOLID principles and provide a real-world scenario where you refactored code to comply with the Single Responsibility Principle.

Approach

  1. State each principle in one line with a concrete example: Single Responsibility (one reason to change), Open/Closed (add behaviour by adding a class, not editing a switch), Liskov Substitution (a subtype must honour the base contract; the Square that breaks Rectangle's setters is the classic violation), Interface Segregation (small role interfaces), Dependency Inversion (high-level code depends on abstractions).
  2. Choose a refactoring story with a clear before and after, such as an OrderService that validated input, wrote SQL, sent email and formatted logs, split into a validator, a repository, a notifier and the service that coordinates them.
  3. Explain the reason for the split as reasons to change: the email template, the schema and the validation rules changed for different people at different times, and each split class could then be unit tested on its own.
  4. Give the trade-off: splitting too far creates many tiny classes and indirection, so I stop when each class has one reason to change, not one method.
  5. Close with a result you can check, such as a bug fix in one area that no longer touched the others, or test coverage the split made possible.

Follow-up

  • Design a notification sender for email, SMS and push so that adding a new channel does not modify existing code.
  • Give an example of a Liskov Substitution violation you have seen and how you fixed it.
  • What is the difference between Dependency Inversion and dependency injection?

How does Dependency Injection promote loose coupling, and how do you implement it in your projects?

Medium · Object-Oriented Design & SOLID Principle

Approach

  1. Define it: a class receives its dependencies (usually interfaces, through the constructor) instead of creating them with new, so it depends on a contract and the concrete type is chosen in one place, the composition root.
  2. Explain the coupling benefit concretely: OrderService takes IPaymentGateway, so tests pass a fake or a Moq mock, and switching providers changes one registration rather than every caller.
  3. Show the implementation: in ASP.NET Core, register with builder.Services.AddScoped<IOrderRepository, SqlOrderRepository>() and take the interface in the controller's constructor; in MVC 5, plug a container such as Autofac or Unity into DependencyResolver.
  4. Know the lifetimes and the trap: transient (new each time), scoped (one per request, the usual choice for a DbContext), singleton (one per app). A singleton that holds a scoped service is a captive dependency; ASP.NET Core's scope validation throws on it in Development.
  5. Name the anti-patterns: service locator (resolving from the container inside business code hides dependencies), and a constructor with many parameters, which usually means the class has too many responsibilities.

Follow-up

  • A singleton cache needs a DbContext. How do you give it one safely?
  • How would you unit test a controller that depends on three services?
  • When is constructor injection the wrong choice, and what would you use instead?

Explain the difference between an abstract class and an interface. When would you choose one over the other?

Medium · Object-Oriented Design & SOLID Principle

Approach

  1. State the mechanical differences: an abstract class can have fields, constructors, implemented methods and access modifiers, and a class can inherit only one; an interface declares a contract with no instance state, and a class can implement many.
  2. Note the modern detail: C# 8 and Java 8 allow default method implementations in interfaces, but interfaces still cannot hold instance fields, so shared state still needs a base class.
  3. Give the rule for choosing: an interface for a capability that unrelated types share or that I want to mock and inject (IEmailSender, IRepository<T>); an abstract class when a family of types shares state and steps, as in a template method where the base fixes the algorithm and subclasses fill in steps.
  4. Mention versioning: adding a member to an interface breaks every implementer unless it has a default implementation, while adding a concrete method to an abstract base does not.
  5. A common combination: an interface as the public contract plus an optional abstract base class that implements the shared parts.

Follow-up

  • Can an abstract class have a constructor? Who calls it?
  • Do default interface methods make abstract classes unnecessary?
  • Why do dependency injection containers and mocking libraries work better with interfaces?

Describe a design pattern (e.g., Repository, Factory, or Singleton) you have recently implemented and explain

Medium · Object-Oriented Design & SOLID Principle

Describe a design pattern (e.g., Repository, Factory, or Singleton) you have recently implemented and explain why it was the right choice for that problem.

Approach

  1. Structure the answer: the problem, the pattern, the alternatives considered, why this one fit, what it cost, and the result. Give the problem before naming the pattern.
  2. Repository: put data access behind an interface such as IOrderRepository so business logic and tests do not depend on the ORM. Name the trade-off: with Entity Framework, DbContext already acts as a unit of work and repository, so a thin generic repository over it can add little value.
  3. Factory: use it when the concrete type depends on input at runtime, for example choosing a payment or notification handler by type. A dictionary of type to handler, registered through DI, keeps it open for extension instead of a growing switch.
  4. Singleton: one shared instance, such as a configuration cache. Name the costs: hidden global state, harder testing, and thread safety (Lazy<T> handles lazy thread-safe creation); registering a singleton lifetime in the DI container usually beats a static Instance property.
  5. End with evidence: what became easier afterwards (adding a provider, testing without a database) and what you would do differently.

Follow-up

  • What would you have built without the pattern, and why was that worse here?
  • How did you test the code that uses the pattern?
  • When have you seen this pattern overused, and how would you remove it?

How do you prevent tight coupling when designing communication between different software modules?

Medium · Object-Oriented Design & SOLID Principle

Approach

  1. Depend on abstractions at module boundaries: the consuming module defines the interface it needs, and the providing module implements it, wired together by dependency injection.
  2. Share contracts, not internals: pass DTOs or events across the boundary rather than entity classes, and never let one module read another module's tables directly.
  3. Choose synchronous or asynchronous on purpose: a direct interface call when the caller needs an answer now; an event (observer or mediator in-process, a message queue between services) when the sender should not know who reacts, accepting eventual consistency and harder tracing.
  4. Put a facade or adapter in front of external or legacy systems so their model does not leak into the rest of the code, and version contracts so one side can change without breaking the other.
  5. Name how you would detect coupling: a change that ripples across several modules, circular project references, or tests that need half the system to run.

Follow-up

  • When would you replace a direct call with an event, and what do you give up?
  • How do you change a shared contract without breaking the modules that use it?
  • How would you split a module that two others both depend on heavily?

Explain how you would debug a memory leak in a production application.

Medium · Problem-Solving, Coding & Aptitude

Approach

  1. Confirm it is a leak first: memory that keeps growing under steady load and does not drop after full garbage collections, as opposed to a cache warming up or a traffic spike. Plot process memory and GC heap size against time and against deploys.
  2. Decide whether it is managed or native memory: if the GC heap is flat but private bytes keep rising, look at native allocations, unmanaged handles or undisposed resources rather than object references.
  3. Take two heap snapshots some time apart (dotnet-gcdump or dotnet-dump for .NET, jmap or a heap dump for the JVM) from one instance taken out of the load balancer, since a dump can pause the process. Compare object counts by type to see which types keep growing.
  4. For the growing type, follow the retention path to a GC root (gcroot in dotnet-dump, the dominator tree in Eclipse MAT). Typical causes: a static dictionary or cache with no size limit or expiry, event handlers never unsubscribed, timers holding references, or a singleton capturing scoped objects.
  5. Contain, then fix: recycle instances as a stopgap, fix the reference, reproduce with a load test to confirm memory is flat, and add a memory alert so a regression is caught early.

Follow-up

  • The managed heap is flat but the process keeps growing. What do you look at next?
  • How do you tell a real leak from a cache that is simply large?
  • How would you catch this kind of leak before it reaches production?

Suggested practice checklist

The plan follows the loop: a first day for the written screen, three days on the MVC and design material candidates report, a coding and debugging day, and a final day for behavioral stories and a full mock. Each day names the reported questions to practise.

Day 1 — Map the loop and warm up the aptitude screen

  • Write the three reported stages (Initial Touchpoint, Technical Evaluations, Behavioral Discussion) with the question types each is likely to contain, and mark your weakest area.
  • Do a mixed set of quantitative and logical aptitude problems against a timer you set, covering percentages, ratios, number series, work-and-time and deduction, and review every wrong answer.
  • List the questions you would ask the recruiter about the team's frontend, backend and full-stack balance.

Deliverable: A one-page loop map with your weakest area marked, a set of solved aptitude problems with the errors analysed, and a list of recruiter questions.

Day 2 — MVC request lifecycle and routing

  • Draw the pipeline for a POST request from memory, from the server entry point to the rendered view, and write one sentence for every stage.
  • Configure a conventional route, a custom route with a constraint and an attribute route, and explain how the engine decides between them.
  • Build a small controller with a view and trace a request through it in the debugger.

Deliverable: A pipeline diagram, a working custom-route example, and a spoken two-minute answer for each of the lifecycle and routing questions.

Day 3 — Filters, state and global exception handling

  • Write a custom action filter that logs execution time for selected actions, and register it on one action and then globally.
  • Make a table comparing TempData, ViewData and ViewBag by lifetime, typing and typical use, with a post-redirect-get example.
  • Set up global exception handling for both page and API requests, and add anti-forgery tokens and server-side input validation to one form.

Deliverable: A working action filter, a TempData, ViewData and ViewBag comparison table, a global error handler with page and JSON responses, and a spoken answer on the four filter types.

Day 4 — SOLID, dependency injection and loose coupling

  • Take a class that validates, queries and sends email, and split it by the Single Responsibility Principle; write down each principle with one line of code.
  • Build the Email, SMS and Push notification module so that a new channel is a new class only.
  • Refactor a tightly coupled class to constructor injection, register it in a container with the right lifetime, and unit-test it with a fake.
  • Write down three ways to keep modules decoupled: interfaces, events or messages, and a mediator.

Deliverable: Before-and-after code for the SRP refactor, the notification module with a test, and a written answer on decoupling that names three techniques.

Day 5 — Abstract classes, interfaces, patterns and SQL

  • Write a comparison of abstract class and interface (state, multiple inheritance, default methods, versioning) and a rule for choosing between them.
  • Implement Repository, Factory and Strategy on one small domain, then write when each is overkill; note the Singleton versus container-registered single instance difference.
  • Pick the pattern you used on a real project and write its story: problem, alternatives, trade-off.

Deliverable: An abstract class versus interface decision note, three small pattern implementations with a when-not-to-use note each, one pattern story, and a solved SQL practice item.

Day 6 — Coding and debugging

  • Write the iterative linked-list reversal and Floyd's cycle detection, then test with empty, single-node and two-node lists.
  • Take a nested-loop pair-finding problem and rewrite it with a hash set to reach linear time; write the palindrome check with two pointers and constant extra space.
  • Solve the first non-repeating character in a single pass and a recursive flatten of a nested structure, and state time and space for each.
  • Write a step-by-step memory-leak investigation: confirm growth, take snapshots, compare retained objects, and find the root reference.

Deliverable: Tested code for five problems with complexity stated for each, and a written memory-leak runbook you can narrate.

Day 7 — Behavioral stories and a full mock

  • Write four stories for the reported prompts on changing requirements, why Aezion, a technical disagreement and competing deadlines, each with situation, decision and result.
  • Write a short why-Aezion answer that cites something specific from the company's public material and one project of yours.
  • Run a full mock: aptitude warm-up, the pipeline diagram from memory, one design exercise and one coding problem aloud, then the four stories.

Deliverable: Four written behavioral stories, a short why-Aezion answer, and a notes page from the mock listing the three weakest answers to fix.

Behavioral round: stories for client work

Candidates report the final stage as a discussion of soft skills, cultural alignment and understanding of the company's business. The reported prompts are about working with unclear or shifting requirements, motivation for joining, a technical disagreement, and several deadlines at once. For a role that works with product managers, QA and client stakeholders, each story should show you making a decision, communicating it, and living with the result.

  1. Tell me about a code review where you changed your approach after feedback, or where you had to give difficult feedback to a peer.

How do you handle exception handling globally within an MVC pipeline?

Medium · MVC Framework & Web Architecture

Approach

  1. Start with layers. A try/catch inside an action is for failures you can recover from or translate, such as a missing record becoming a 404. Everything else should reach one global handler, so no controller carries its own error plumbing.
  2. Name the mechanisms. ASP.NET Core: exception-handling middleware (UseExceptionHandler with an error route) catches anything thrown below it, even outside MVC, while a global exception filter only sees failures inside controllers, actions and action filters. Classic ASP.NET MVC: a global HandleError attribute, plus Application_Error in Global.asax for what filters miss. Spring MVC: a controller advice with exception handler methods.
  3. Shape the response by caller. Page requests get an error view, API calls get a ProblemDetails JSON body with the right status. Map known domain exceptions to 400, 404 or 409, treat the rest as 500, and show stack traces only in development.
  4. Log once at the boundary with a correlation or trace id instead of catch-log-rethrow in every layer, and show that id on the error page so support can find the entry. Add status-code pages for 404s, which are not exceptions.
  5. Edge cases: once the response has started you cannot change its status or body, the error handler can itself throw, and validation failures belong in model state, not exceptions.

Follow-up

  • What is the difference between an exception filter and exception-handling middleware, and when would you need both?
  • How would you return JSON for API calls but a friendly page for browser requests from the same app?
  • What do you do when the exception happens after the response has started streaming?

Describe a time when you had to work with a vague or changing set of project requirements. How did you handle

Medium · Soft Skills & Behavioral

Describe a time when you had to work with a vague or changing set of project requirements. How did you handle it?

Approach

  1. Pick a project where the requirements really moved, and state the situation in two sentences: what was unclear, who owned the answer, and what was at stake if you guessed wrong.
  2. Show the method, not just the feeling: you wrote down your assumptions, listed the open questions, took them to the person who could decide (product manager, client stakeholder or QA), and confirmed the answers in writing so the next change had a reference point.
  3. Show the engineering response. You delivered a thin slice early to get feedback, and isolated the volatile parts behind interfaces or configuration so a change meant adding a class, not rewriting several. This is the Open/Closed idea applied to a real change.
  4. Close with the result and one lesson: what shipped, what changed afterwards, and what you now do in the first week of a vague project. The story fails if you only waited for clarity or built the full feature on a guess and had to redo it.

Follow-up

  • What did you do when two stakeholders gave conflicting requirements?
  • How did you decide what to build first while the details were still open?
  • How do you keep a codebase from becoming a patchwork when requirements keep changing?

Why are you interested in joining Aezion, and how do you see your skills aligning with our company vision?

Medium · Soft Skills & Behavioral

Approach

  1. Build the answer from three parts: what attracts you about custom software for client businesses, which of your skills and projects match the stack the role describes (MVC web applications, C# or Java, SOLID, SQL, unit tests), and what you want to learn there.
  2. Research before the interview and cite something specific you actually found: the kinds of clients, the industries, a project write-up or the way the company describes its work. Candidates are advised to read up on the company's history, client stories and business model, so a vague answer stands out.
  3. Tie one concrete project to the match. For example: I built a modular MVC application with dependency injection and unit tests, and I want to keep doing that for varied clients. Avoid claims about the company's culture that you cannot support.
  4. Keep it short and honest, and finish with a question about the team's current focus. Generic praise, or an answer that could be about any employer, fails here.

Follow-up

  • What do you know about the clients and industries the company works with?
  • Which kind of project would you want to work on first, and why?
  • Where do you want to be technically in two years, and how does this role get you there?

Walk me through a situation where you had a technical disagreement with a teammate. How did you reach a resolu

Medium · Soft Skills & Behavioral

Walk me through a situation where you had a technical disagreement with a teammate. How did you reach a resolution?

Approach

  1. Choose a real technical disagreement with a concrete subject, such as whether to introduce a repository layer, synchronous versus asynchronous processing, or how to split a class. Name the teammate's position fairly, in terms they would accept.
  2. Show how you tried to resolve it with evidence and not with rank: a small prototype, a benchmark, a test that exposed the difference, a look at the existing codebase, or a third reviewer.
  3. Say what was decided and what happened afterwards. The strongest version includes a part where you changed your mind or conceded a point, and a working relationship that stayed intact.
  4. The mistake that sinks it is a story where you were right and the other person was careless, or one with no resolution. Mention how you kept the discussion in the code review or design thread, not in private messages.

Follow-up

  • What if the teammate still disagreed after you showed the data?
  • Have you ever changed your position in a disagreement like this?
  • How do you handle disagreement with a more senior engineer in a code review?

How do you manage your time and prioritize tasks when working on multiple tight deadlines simultaneously?

Medium · Soft Skills & Behavioral

Approach

  1. Describe a real week with several deadlines at once. List each task with its deadline, who is waiting on it, the cost of it slipping, and the effort left, then rank by cost of delay and dependencies, not by volume of noise.
  2. Show that you took conflicts to the person who sets priorities (product manager or project lead) and asked for a ranking, instead of settling it silently.
  3. Explain how you protected the highest-priority work: cutting scope to a shippable version, deferring the rest openly, blocking focused time, and splitting work so QA or a teammate could start in parallel.
  4. Give the outcome with specifics, including what you dropped or delayed and how you told people early. Saying that you work longer hours and multitask is the answer that fails. Include how you handle an urgent production bug arriving mid-sprint.

Follow-up

  • What do you do when two stakeholders each insist their task is the top priority?
  • How do you tell stakeholders you are going to miss a deadline, and when?
  • How do you decide what to cut when the scope does not fit?