Interview guides · Amrut Software
Amrut Software Software Engineer Interview Guide
Amrut Software Software Engineer interview guide: the reported rounds, OOP, Frappe debugging, DevSecOps and Vue.js questions, and a 7-day plan.
The 60-second brief
Candidates describe Amrut Software as a software services company. They name three engineering areas: DevSecOps, Forge development, and full-stack applications built on the Frappe framework, with Python on the back end and Vue.js on the front end. Candidates describe the projects as serving both internal operations and client deliveries. They describe the role as turning business needs into technical solutions, with design reviews, code audits and close work alongside project managers and other engineers.
This guide is for software engineers interviewing at Amrut Software, whether your background is Python back-end work, full-stack development or pipeline and security work. The requirements candidates describe list Python (or a similar back-end language), object-oriented programming, full-stack or services experience and the ability to explain your projects clearly as must-haves. Frappe, DevSecOps or automated testing, and Vue.js are listed as nice-to-haves. The guide covers each reported round, how to answer each reported question in depth, the mistakes that cost candidates on these question types, and a seven-day plan.
Candidates report three rounds over roughly three to five weeks: Background Screening, then Technical Assessments (written assessments plus deep dives), then a Team Fit Conversation.
Focus areas
- DevSecOps
- Python
- Frappe Framework
Overview
The reported questions fall into two groups. The first is technical foundations: OOP principles in your own code, debugging inside the Frappe framework, how front end and back end were integrated in your recent projects, and how you build security into the development lifecycle. The second is project experience and scenarios: a challenge from your last project, optimising an architecture with a latency problem, managing technical debt against delivery dates, and changing course when requirements moved. The reported questions are discussion-style rather than puzzle-style. The common thread is explaining real systems you built and the reasons behind them.
That changes how you should prepare. Candidates report that fundamentals such as OOP matter most in the early rounds, and that project questions go deep: why you picked a library, what constraints you worked under, what your own contribution changed. Candidates also report being asked to draw diagrams or walk through the architecture of their projects. Before the loop, have one or two projects you can sketch from memory, with the request path, the data model and the deployment pipeline, and a reason for every box on the diagram.
The stack candidates name is Python, the Frappe framework, Vue.js, and DevSecOps with a focus on security in CI/CD. If you have not used Frappe, learn its core model before the loop: DocTypes, controller hooks such as validate and on_submit, hooks.py doc_events, whitelisted methods exposed under /api/method/, and the bench CLI. That makes the Frappe debugging question something you can answer concretely. For the DevSecOps side, be able to say where in a pipeline each control runs (secret scanning, dependency audit, static analysis, image scanning) and what makes the build fail.
Candidates describe the tone as conversational and professional, and report that memorised answers lead to follow-up questions that test real understanding. Practise speaking about your own work rather than rehearsing scripts: for each reported question, know your concrete example and the one or two alternatives you rejected.
The Amrut Software interview process
Candidates report these three stages in this order across roughly three to five weeks, and say the hiring team may tailor its technical questions to its current projects.
Background Screening
Candidates describe the first stage as a review of your background against what the role needs. Be ready to discuss any technology on your resume in depth, and cut anything you cannot explain. Lead with the parts of your experience that match what candidates list as requirements: Python or another back-end language, OOP, full-stack or services delivery, and any Frappe, Vue.js, DevSecOps or automated-testing work.
What to demonstrate
- How your background lines up with the role's needs, including the reported must-haves: Python or a similar back-end language, OOP, full-stack or services experience
- Relevant exposure to the nice-to-haves candidates list: Frappe, Vue.js, DevSecOps or automated testing
How to prepare
- Go through your resume line by line and, for every technology listed, write one sentence on where you used it and why it was chosen over an alternative.
- Write a short spoken summary of each of your two main projects: the problem, the stack, your own part, and one measurable result.
- If you have no Frappe experience, build a small Frappe app with one custom DocType and a validate hook so you can talk about the framework from use, not reading.
Technical Assessments
Candidates report a series of technical rounds here, including written assessments and deep dives, held face to face or virtually. The reported technical questions cover OOP, Frappe debugging, front-end and back-end integration, security in the development lifecycle, and scenario questions such as reducing latency in an existing architecture. Candidates also report being asked to sketch or explain the architecture of their own projects. Answer structurally: state the approach, then the reasoning, then the trade-off, and think out loud when given a scenario.
What to demonstrate
- Core fundamentals in writing and aloud, with OOP the topic candidates name most
- Depth on the stack candidates report: Python, Frappe, Vue.js and security in CI/CD
- Ability to explain and diagram the architecture of a past project and defend its design choices
- Structured reasoning through scenario questions such as a latency problem
How to prepare
- Write answers on paper to the OOP question using classes from your own code, including one Python-specific point such as the MRO or name mangling.
- Practise drawing one project's architecture in under a page: client, API layer, background jobs, database, cache and CI/CD pipeline, with a reason for each part.
- Rehearse the Frappe debugging walk-through and the pipeline security answer aloud until you can name each tool and step without notes.
- Work the latency scenario end to end: measure, locate, fix, verify, and name what each fix costs.
Team Fit Conversation
Candidates describe the last stage as a Team Fit Conversation about how you fit within the team and line up with the company's broader goals. As preparation, bring stories where your own decision is visible, for example on changing requirements, technical debt under a deadline, or a disagreement over technical direction. Ask the recruiter early which area the role covers: Frappe applications, Forge development or DevSecOps. If it is Forge work, ask what that work involves and which technologies it uses, so you can review them before the loop.
What to demonstrate
- Fit within the team
- Alignment with the company's broader goals
How to prepare
- Prepare STAR stories for changing requirements, technical debt under a deadline and a disagreement over technical direction, each with what you decided and what it cost.
- Write three questions about the team: which of Frappe, Forge or DevSecOps work you would join, what the Forge work involves, and how code reviews and audits run.
Where candidates lose points
Reciting the four OOP pillars as textbook definitions with no code from your own projects
For each principle, prepare one class you actually wrote, such as a Frappe controller overriding validate or a service with an abstract base class. Add one Python-specific detail, such as name mangling, @property or MRO, so the answer shows you know how Python implements these ideas.
Answering the latency scenario with 'add a cache' before saying where the time goes
Open with measurement: percentiles, tracing, the slow query log, EXPLAIN. Then go through the likely causes in order (N+1 queries, missing indexes, synchronous slow work, unbounded queries) and name the staleness or consistency cost of every fix you choose.
Listing security tools for the DevSecOps question without saying where they run or what they block
Place each control in the pipeline (pre-commit, pull request, build, deploy) and say what fails the build. Add the application side: parameterised frappe.db.sql calls and permission checks inside whitelisted methods. Have an answer ready for a leaked secret: rotate first, then remove it from history.
Describing a project you cannot defend when asked why a library or architecture was chosen
Candidates report deep-dive questions on resume projects. For every project you list, know the alternative you rejected and why, the constraint that drove the design, and your own contribution. Practise drawing its architecture from memory.
Telling behavioral stories in 'we' so your own decision never appears
For the challenge, technical-debt and changing-requirements stories, write down the one decision you personally made and the trade-off it carried. Say 'I' for your actions, give a measurable result, and have a 'what I would do differently' line ready.
Questions & practice
Find overlapping job attempts and peak concurrency from lease records
Medium · sweep line · intervals · leases
A day of job_run history yields about 50,000,000 attempt records: (job_run_id, job_type, attempt, started_at, finished_at which is NULL when the worker died, lease_expires_at). Leases expire on a clock, so a job that outran its lease ran twice. Produce (a) every job_run_id whose attempts overlapped in wall-clock time and (b) the peak number of simultaneously running attempts per job_type with the minute it occurred. Target O(n log n). State how you treat a NULL finished_at and what clock skew does to your answer.
Approach
- Define the interval before sorting anything: an attempt occupies [started_at, COALESCE(finished_at, lease_expires_at)). finished_at is observed and lease_expires_at is only a promise, so every attempt without a finish contributes an estimate and the whole result is a lower bound on overlap rather than an exact count.
- For peak concurrency, sweep: emit 2n endpoints, sort by (timestamp, kind) with ends ordered before starts at equal timestamps, then walk the sequence maintaining a counter per job_type and record each type's maximum with its timestamp. O(n log n) dominated by the sort, O(n) space, or O(1) extra if the sort is external and the walk streams.
- For overlap detection, do not compare attempts pairwise. A single global sort by (job_run_id, started_at) gives both the grouping and the order; within a group, keep the maximum end seen so far and report an overlap exactly when the next start is less than that running maximum, which is one linear pass after the sort.
- Half-open intervals matter and are easy to get wrong: with closed intervals an attempt ending at the same millisecond another begins reads as concurrency two, and across 50,000,000 records that artefact swamps the real signal.
Follow-up
- A handler is not idempotent and you have found 400 overlapping jobs. Which of them actually caused damage, and what would you query to find out?
- Peak concurrency for one job_type is 4 against a configured cap of 4. Is the cap working, or is the data hiding attempts that never started?
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?
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?
Find version gaps and relay lag with window functions
Medium · window functions · event tables · frame semantics
outbox_event holds event_id, aggregate_type, aggregate_id, aggregate_version, event_type, payload, status ('pending','published','dead'), attempts, created_at, published_at. A projection is missing rows and you must decide whether the relay skipped events or the consumer dropped them. Write three queries over the last seven days: one listing every aggregate_id whose published aggregate_version sequence has a hole, one giving per-day counts with a running total, and one returning the newest published event per aggregate. For each, say where the window function is evaluated relative to WHERE and LIMIT. PostgreSQL 16.
Approach
- Gaps: compute lead(aggregate_version) OVER (PARTITION BY aggregate_id ORDER BY aggregate_version) in a subquery, then filter next_version <> aggregate_version + 1 in the outer query. Window functions are evaluated after WHERE, GROUP BY and HAVING and before the outer ORDER BY and LIMIT, so the predicate cannot sit in the same WHERE clause and PostgreSQL 16 has no QUALIFY.
- Say what the seven-day filter does to the answer: it truncates every partition, so the first row per aggregate has no predecessor inside the window and a hole spanning the boundary is invisible. Widen the window, or join to resource.version as the authority for the true maximum.
- Running total: SELECT date_trunc('day', created_at) AS d, count() AS n, sum(count()) OVER (ORDER BY date_trunc('day', created_at) ROWS UNBOUNDED PRECEDING). An aggregate inside a window call is legal because grouping runs before windowing. The grouping key is unique per row here so ROWS and RANGE agree, but write the frame anyway — over ungrouped rows with tied timestamps the default RANGE frame pulls in every peer row and the total jumps.
- Newest per aggregate: DISTINCT ON (aggregate_id) ... ORDER BY aggregate_id, aggregate_version DESC is the cheap PostgreSQL-only form when an index matches that order; row_number() OVER (PARTITION BY aggregate_id ORDER BY aggregate_version DESC) = 1 is the portable form and needs a subquery for the same evaluation-order reason as the gap query.
Follow-up
- Relay failover redelivers events. Does a duplicate break the gap query, and how would you detect one from this table alone?
- Turn the gap check into a continuous monitor rather than a query someone runs after an incident. What does it watch?
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?
Can you explain the core principles of OOPS and how they apply to your code?
Medium · Technical Foundations
Approach
- Name the four principles, then tie each to code you wrote. Encapsulation: a class that owns its state and exposes methods instead of raw fields. Abstraction: an interface that hides an implementation, such as a PaymentGateway base class. Inheritance: a subclass reusing and specialising behaviour. Polymorphism: calling the same method on different types and getting type-specific behaviour.
- Make it Python-specific. There is no enforced private access: a single underscore is a convention, and a double underscore triggers name mangling to _ClassName__attr. @property lets you add validation without changing the call site. abc.ABC with @abstractmethod enforces an interface. Duck typing provides polymorphism without a shared base class.
- Give a real example from your stack. In Frappe, a DocType controller subclasses Document and overrides validate or on_submit. That is inheritance plus a template-method pattern: the framework calls your hook at a fixed point in the save lifecycle.
- Show judgement on the trade-off. Prefer composition when the relationship is 'has a' rather than 'is a', keep hierarchies shallow, and respect Liskov substitution: a subclass that throws on a method its parent supports breaks every caller written against the parent.
Follow-up
- In a diamond inheritance, which parent's method does Python call, and how does super() decide?
- What is the difference between abstraction and encapsulation, in one example from your code?
- Show me a place where you replaced inheritance with composition. What problem did it solve?
What are the primary differences between the front-end and back-end integration in your recent projects?
Medium · Technical Foundations
Approach
- Describe one real integration end to end: a Vue.js front end calling a Python back end over JSON. In Frappe that means /api/resource/<DocType> for CRUD and /api/method/<dotted.path> for @frappe.whitelist functions. Name who owns the contract and how changes to it were coordinated.
- Contrast the two sides precisely. Front end: rendering, client-side state (component state or a store such as Pinia), optimistic updates, input validation for user experience. Back end: authoritative validation, permissions, transactions, business rules. Never trust the client: every check that matters is repeated on the server.
- Cover auth and transport. Session cookie plus CSRF token for same-origin Desk-style apps, token or OAuth for external clients, CORS for cross-origin calls, and how the front end handles a 401 or 403 mid-session.
- Cover failure and scale: a consistent error shape the UI can branch on, pagination for list endpoints, debouncing search calls, and idempotency for retried writes so a double-click or network retry does not create two records.
- Finish with a trade-off you actually made, such as server-rendered pages versus a Vue SPA, or one coarse endpoint versus several fine-grained ones, and why.
Follow-up
- Where does validation live in your design, and what happens if the front-end check and the back-end check disagree?
- How do you change an API response shape without breaking a front end that is already deployed?
- A list page is slow. How do you tell whether the problem is the API, the network or the rendering?
Describe how you ensure security in your development lifecycle.
Medium · Technical Foundations
Approach
- Walk the lifecycle in order. Design: threat-model new features (who can call this, with what data, what if they lie). Code: parameterised queries (in Frappe, pass values to frappe.db.sql instead of formatting strings), explicit permission checks inside whitelisted methods, care with allow_guest, output escaping in templates.
- Pipeline (security in CI/CD): secret scanning such as gitleaks on every push, dependency audits with pip-audit and npm audit or Dependabot, static analysis such as Bandit or Semgrep, container image scanning such as Trivy if you ship images. Say which findings fail the build (for example high-severity or known-exploited) and which only report.
- Pipeline hardening itself: least-privilege CI tokens, secrets in the CI secret store or a vault rather than the repository, protected branches with required review, pinned dependency versions and lockfiles so builds are reproducible.
- Deployment and runtime: environment separation, patching cadence for base images and frameworks, audit logging, and a dynamic scan such as OWASP ZAP against a staging deployment.
- Give one concrete example from your work: a vulnerability caught by a control, or one that slipped through and the control you added afterwards.
Follow-up
- A developer committed an API key to the repository. What do you do, in order?
- The first dependency scan reports two hundred findings. How do you triage without blocking every merge?
- How do you stop a whitelisted method from leaking records a user should not see?
Walk me through a specific challenge you faced in your last project and how you resolved it.
Medium · Project Experience & Scenario Analysis
Approach
- Use STAR, but spend most of the time on the Action. Situation and Task in a few sentences: the system, the constraint (deadline, scale, legacy code, unclear requirement) and why it was hard.
- Pick a challenge where you made the key decision. Explain the options you considered, the one you chose and why the others lost. Candidates report deep follow-ups on why libraries and architectures were chosen, so know the alternatives.
- Be ready to draw it: the components involved, where the problem sat and what your change touched.
- Close with a measurable result (latency, error rate, delivery date met, support tickets reduced) and what you would do differently. Using 'we' throughout hides your contribution, so say 'I' for your own actions.
Follow-up
- What alternative did you reject, and what would have happened if you had chosen it?
- How did you confirm your fix actually resolved the problem rather than masking it?
- If you had twice the time, what would you have done differently?
Given a specific scenario involving system latency, how would you optimize the current architecture?
Medium · Project Experience & Scenario Analysis
Approach
- Clarify, then measure. Ask which operation is slow, for whom, and since when. Look at p50, p95 and p99, not the average. Use tracing or APM, the slow query log and EXPLAIN to break request time into client, network, application, database and external calls before changing anything.
- Fix in order of cost and likelihood. N+1 queries: batch or join. Missing indexes on filter and sort columns. Unbounded result sets: paginate. Synchronous slow work such as emails, PDF generation and third-party calls: move it to a background queue (frappe.enqueue in Frappe). Then cache hot reads in Redis with a defined TTL and invalidation rule.
- Then scale structurally if needed: connection pooling, read replicas for heavy reporting queries, a CDN for static assets, horizontal app scaling behind a load balancer.
- State each trade-off. Caching adds staleness and invalidation bugs. Async processing changes the user experience from 'done' to 'accepted'. Replicas add replication lag. Denormalising speeds reads but complicates writes.
- Verify with the same measurement: a before-and-after load test at realistic concurrency, plus a latency alert so a regression is caught.
Follow-up
- Only p99 is bad while p50 is fine. What does that suggest, and where do you look?
- After you add a cache, users see stale data. How do you fix invalidation?
- The database is still the bottleneck after caching. What next?
How do you manage technical debt while adhering to strict project delivery timelines?
Medium · Project Experience & Scenario Analysis
Approach
- Separate deliberate debt (a known shortcut taken to hit a date) from accidental debt (design that aged badly). Deliberate debt is acceptable when it is recorded, bounded and has a repayment trigger.
- Make it visible: a ticket for each shortcut with its cost in concrete terms (slower changes, incident risk, manual work), so it can be prioritised against features with a project manager rather than argued as taste.
- Bound the damage when you ship the shortcut: tests around the fragile part, a feature flag, a clear interface so the hack stays local, and a condition that forces the fix (for example, the next change to that module, or a data-volume threshold).
- Pay it down continuously: improve code you are already touching, reserve some capacity in each iteration, and prioritise debt that blocks upcoming features or causes repeat incidents.
- Back it with a real example: a shortcut you chose, how you contained it, and whether and when it was repaid.
Follow-up
- Your project manager refuses to schedule refactoring. How do you make the case?
- How do you decide which debt not to pay off at all?
- Tell me about debt you took on that turned out to cost more than you expected.
How do you approach debugging a complex issue within the Frappe framework?
Medium · Technical Foundations
Approach
- Reproduce first. Get the exact document, user, role and action that fails, then reproduce on a local bench with similar data and developer_mode enabled in site_config.json. If it only fails in production, check the Error Log list and the bench logs folder (web, worker and scheduler error logs) for the traceback.
- Locate the code path. Find the DocType controller hooks (validate, before_save, on_submit), every doc_events entry in hooks.py across all installed apps, override_doctype_class, and any Server Scripts or Client Scripts configured in Desk. Desk-configured scripts are not in your repo and are a common blind spot.
- Inspect interactively. Open bench --site <site> console, load the record with frappe.get_doc, call the failing method and inspect state. For UI issues, read the /api/method/ or /api/resource/ call in the browser network tab and the exception returned with it.
- Rule out framework-level causes: stale cache (bench clear-cache), schema not migrated after a DocType change (bench --site <site> migrate), permission differences between roles or User Permissions, and background jobs run by RQ workers rather than the request.
- Close the loop. Fix at the root, add a regression test runnable with bench run-tests, and use frappe.log_error with context in fragile paths so the next failure arrives with data.
Follow-up
- A save works for an Administrator but fails for a normal user. Where do you look first?
- The failure only happens in a background job, never in the request. How do you debug it?
- Two installed apps hook the same DocType event. How do you find out which runs first, and how do you control it?
Suggested practice checklist
Each day targets one reported round or question group, using the stack candidates name (Python, Frappe, Vue.js, security in CI/CD), and ends with something written or recorded that you can review on the last day.
Day 1 — Resume audit and project inventory for Background Screening
- Go through your resume line by line and write, for each technology, where you used it and the alternative you rejected.
- Pick your two strongest projects and write a short summary for each: problem, stack, your part, one measured result.
- Outline the challenge story for the last-project question in STAR form, marking the decision that was yours.
Deliverable: A resume rationale sheet, two project summaries, and an outlined STAR story for the last-project challenge question.
Day 2 — OOP in Python, from your own code
- Write a small Python example showing encapsulation (@property), abstraction (abc.ABC), inheritance with super() and polymorphism through duck typing.
- Find one class in your past work for each principle and note it.
- Answer the OOP question aloud, then answer the diamond-inheritance and composition follow-ups.
Deliverable: A runnable OOP example file, a four-row mapping of principles to your own classes, and a recorded spoken answer.
Day 3 — Frappe internals and debugging
- Set up a local bench, create a custom DocType with a validate hook and a whitelisted method, then break it on purpose.
- Debug the break using bench console, the Error Log, the bench log files and the browser network tab, writing down each step.
- Practise the concurrent-edit update path and connect it to Frappe's check that a document was modified after it was opened.
Deliverable: A working Frappe app with an intentional bug, a step-by-step debugging log, and a written concurrent-edit solution.
Day 4 — Vue.js and back-end integration
- Diagram how a recent project's front end talked to its back end: endpoints, auth, error shape, pagination.
- Write a minimal Vue component that calls a Frappe /api/method/ endpoint and handles loading, error and a 403 response.
- Review Vue itself: reactivity with ref and reactive, computed versus watch, lifecycle hooks such as onMounted, and props down with events up. Then work one scenario, such as a component that re-renders too often, and explain the cause and the fix.
- Answer the front-end/back-end integration question aloud, including where validation lives on each side.
Deliverable: An integration diagram, a small Vue component wired to an API, a written diagnosis of the over-rendering scenario, and a recorded spoken answer.
Day 5 — Security in the development lifecycle and CI/CD
- Draw a CI/CD pipeline and place secret scanning, dependency audit, static analysis, image scanning and a staging dynamic scan on it, marking which findings fail the build.
- Run pip-audit and Bandit (or Semgrep) on one of your own repositories and triage the results.
- Write the leaked-API-key response in order, then answer the security question aloud.
Deliverable: An annotated pipeline diagram, a triaged scan report from your repository, and a written incident checklist for a leaked secret.
Day 6 — Latency scenario and technical debt under deadlines
- Work the latency scenario on paper: metrics to collect, the ordered list of causes, a fix for each, and the cost of each fix.
- Practise the deadline-and-debt question and outline your own example: the shortcut, how you bounded it, and the trigger for repaying it.
- Answer both questions aloud, then the p99-only and stale-cache follow-ups.
Deliverable: A one-page latency playbook, an outlined technical-debt story, and two recorded answers.
Day 7 — Team Fit rehearsal and full mock
- Outline STAR stories for the changing-requirements question and for a disagreement over technical direction you lost and then committed to.
- Run a mock: one project architecture walk-through drawn from memory, two technical questions and two behavioral questions, back to back.
- Write three questions for the team, covering their Frappe, Forge or DevSecOps work and what the Forge work involves.
Deliverable: Two outlined STAR stories, notes from a full mock with your weakest answer marked, and three questions for the team.
Behavioural preparation
The reported behavioral prompts are the pivot after changing requirements, plus the project-challenge and technical-debt questions, with two practice prompts on related themes (technical disagreement and meeting a deadline). Each answer should make your own decision and its trade-off visible, and stand up to follow-up questions about why you chose that path.
- Walk me through a specific challenge you faced in your last project and how you resolved it.
- How do you manage technical debt while adhering to strict project delivery timelines?
- Tell me about a disagreement over technical direction on a project. What was your position, how was it settled, and what did you do afterwards?
- Describe a time you met a deadline without lowering code quality. What did you cut, and how did you keep the risk contained?
Describe a time you had to pivot your approach due to changing project requirements.
Medium · Project Experience & Scenario Analysis
Approach
- STAR with the change at the centre: what you were building, what changed (client request, new constraint, discovered limitation) and how you learned about it.
- Show your decision process: what work could be kept, what had to be discarded, the options you laid out and how you re-estimated. Make your own role explicit.
- Show communication: who you told (project manager, client, team), how you reset expectations on scope or dates, and how you recorded the new requirement so it did not drift again.
- End with the outcome and the lesson, such as building flexibility into the design earlier or validating requirements with a prototype. Answers that blame the client or show no ownership of the response do not work.
Follow-up
- Could you have spotted that requirement change earlier? How?
- How did you keep quality from slipping while you re-planned?
- How did you decide which already-built work to throw away?
Ship under a deadline and bound the debt you chose
Medium · pagination · technical debt · tradeoffs
You have four days to ship a tenant-facing listing endpoint. The version you would defend uses keyset pagination over (tenant_id, status, updated_at DESC, resource_id DESC); the version you can finish uses LIMIT/OFFSET with no matching index. Describe a deadline call you actually made of this shape: what you shipped, what you knowingly deferred, how you bounded the damage with a mechanism rather than an intention, and the specific numeric condition that would force the follow-up. Name who you told and where you wrote it down.
Approach
- Name the deferred failure precisely instead of calling it slow. OFFSET n makes the database produce and discard n rows, so cost grows with page depth; without an index matching the sort, every matching row is read and sorted before the limit applies; and rows inserted between two page fetches shift across the boundary so items are skipped or repeated with nothing in the response to signal it.
- Bound the blast radius with something mechanical rather than a promise: cap maximum page depth, cap page size, restrict the endpoint to one internal caller, or keep it behind a flag. State which failure each cap removes and which it leaves standing.
- Attach a number to the trigger and wire it to an alarm: the first tenant crossing N resources, or the endpoint's p99 crossing its share of the 400 ms budget, so the debt announces itself instead of waiting to be remembered.
- Write it where the next engineer looks, which is the code and the ticket, not a chat message: what was deferred, why, the cap, and the trigger.
Follow-up
- At what page depth does the offset version breach your latency budget, given your page size and row counts?
- What breaks first when you switch to keyset pagination later, and what does a client holding an old page token see?
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?