The Input, Injection & Abuse pillar averages 61.9 out of 100 across the 21 third-party apps I audited in June and July 2026, and 13 had no rate limiting on their most expensive endpoint. Web app security for an app you did not write comes down to 12 controls, each with a symptom, a page and a test.
What web app security covers when you did not write the code
Web app security for one app is 12 controls: server-side validation on every write, typed and private uploads, restricted cross-origin access, production security headers, fixed dependencies, rate limits on public endpoints, an OWASP Top 10 walk, bot protection on public forms, a published disclosure contact, a targeted external test, a hardened AI feature, and edge protection.
Web application security is the same subject under its longer name, and the table below is how I write its requirements for one app. The opener’s numbers come from 21 third-party apps I audited in June and July 2026: a selected set, not a random sample, and no rate for AI-built apps in general. Inside the wider production hardening picture, this area is 12 of the 123 checks, and it answers one question: how the app behaves under hostile input and load.
For a web app you did not write, the web application security best practices come down to the rows below, in order, each linked to the page that goes deep on it.
| Control | What it is | Why it matters |
|---|---|---|
| 1. input validation web app rules | Validate every data-writing endpoint with explicit schemas and safe input/output handling, including injection defenses. | Malformed or hostile inputs can corrupt records or reach other users’ screens. |
| 2. file upload testing | Validate upload types and sizes, enforce bucket permissions, and protect access to stored files. | Weak upload and storage controls can expose documents or accept unsafe files. |
| 3. CSRF mitigation | Restrict CORS to intended origins and check credentialed request behavior alongside authentication and request-forgery protections. | Overly permissive cross-origin access can expose authenticated responses to untrusted websites. |
| 4. content security policy unsafe-inline | Configure and test CSP, HSTS, framing restrictions, and other appropriate browser security headers. | Missing browser protections can increase exposure to script injection, framing attacks, or insecure connections. |
| 5. the npm audit command | Audit dependencies, upgrade or remove known-vulnerable packages, and remove unused packages. | Published package vulnerabilities can remain reachable in an otherwise functional product. |
| 6. rate limiting in API routes | Protect public forms, signup flows, and resource-consuming endpoints with appropriate limits. | Abuse can create spam, availability problems, and unexpected service bills. |
| 7. how to test OWASP Top 10 vulnerabilities | Review the application against the OWASP Top 10 and record findings, fixes, and evidence by category. | Unexamined application risks leave preventable weaknesses in the production foundation. |
| 8. bots submitting our signup form | Add bot protection to public forms using Turnstile, hCaptcha, or an appropriate equivalent. | Automated submissions can pollute accounts, reporting, and outbound communication. |
| 9. a security.txt file example | Publish security.txt and a monitored disclosure contact for vulnerability reports. | A researcher needs a clear way to report an issue to the right person. |
| 10. web application penetration testing | Test the five highest-risk externally reachable attack surfaces, remediate findings, and deliver the methods and evidence. | Implementation review alone may miss behavior exposed through real attack paths. |
| 11. what is prompt injection | For every model-backed feature: prompt-injection defenses, validation of model output before it touches data or users, per-user usage limits, and a timeout with a fallback when the provider is down. | Model calls are a new input surface. Unbounded ones leak data, run up bills, and fail loudly when the provider does. |
| 12. how to set up a web application firewall | Place a CDN or web application firewall in front of the origin, with rate and abuse rules that absorb floods before they reach the application. | Application-layer bot protection never sees a volumetric flood; it takes the app down first. |
For a small business, this table is the app half of a cyber security checklist; the people and device half, such as staff training and laptops, is outside this page.
What is hardening? The word, for a web app
Hardening means reducing what an attacker can reach and what a mistake can cost, by removing risky defaults, closing inputs and adding limits, without adding features. For a web app that already works, hardening is the 12 changes in the table above, each checked by a test.
In software, the meaning of hardening is a smaller attack surface, and application hardening in computer security is that same work aimed at one program rather than a whole server or network. The purpose of hardening a web application is a smaller target that stays small: each default you removed, input you closed and limit you added should have a test that fails if someone undoes it. Application hardening techniques, for this page, are the 12 controls; patching belongs to control 5, and the FAQ below separates the two words. No single control is enough on its own, which is the idea behind defense in depth. The broader sense of the word, hardening the whole app before launch, is the production hardening hub linked above.
What is application security, and what is web security, for the stack an AI builder hands you
Application security is protecting the code and the requests an app handles from misuse; web security is the browser-to-server part of it. An AI builder usually hands you a React or Next.js front end, a Node or Python API and a managed database, so the boundary to guard first is the API.
Software security is the narrower term for the code itself: how it is written, which packages it pulls in and what it does with bad input. For a single page app, React security and Next.js security end up at the same place, because whatever the browser holds, a visitor can read and change, so every check that counts runs on the server. Where that line falls in a generated app is covered in the trust boundary in a vibe coded app, and the reasons builders leave gaps on the wrong side of it are in why AI coding tools ship security holes.
What goes wrong without it
How to secure a web application, in one sentence: validate on the server, limit every public endpoint, keep secrets and admin keys server-side, and put something in front of the web app’s origin. Those four moves are my short list of website security best practices, and the five failures below show what a web app looks like when one of them is missing.
A form field reaches the database unchecked
A record turns up with markup in a name field or a value no form should allow, and the next person who opens that record sees it rendered on their screen. The table’s first row names the cause: input the server never checked can damage the stored record or travel to another user’s screen. This is the pillar behind the 61.9 out of 100 in the opener, averaged across all 21 apps I audited, which I chose rather than sampled, so it describes those apps and nothing wider. The mechanics of the two classic outcomes live on their own pages: SQL injection prevention for queries built from input, and how to mitigate cross-site scripting for markup that runs in someone else’s browser.
One caller can hit the expensive endpoint forever
The first sign is a spike: a pile of signups nobody recognizes, a run of text messages, a wall of AI calls, and then the invoice. The reason is the one in row 6 of the table: when nothing slows a caller down, abuse can bring spam, an app that stops answering and a bill no one planned for. In the same audits, 13 of the 21 third-party apps had no rate limiting on their most expensive endpoint. Take a signup form that sends a verification text through a paid SMS provider. A script calls the signup endpoint again and again, and every call sends a text on the owner’s account, because nothing limits how often one caller can hit it; the abuse arrives as spam and as a bill. The lesson I take from it: every public endpoint that spends money on your account needs a limit before launch, not after the first bill. The fix is control 6, rate limiting in API routes.
A stranger rewrites your AI feature’s instructions
The feature starts answering questions it was never meant to answer, or prints the instructions it was given. In the same audits, 8 of the 14 AI apps had a live prompt-injection path, with untrusted text flowing straight into the model’s instructions; 14 is its own count, the audited apps that had an AI feature, not the full 21. Row 11 of the table gives the reason: a model call is one more way in for input, and one with no bounds leaks data, runs up bills and fails loudly when the provider does. What prompt injection is, and how it reaches a model, has its own page, linked from that row.
A package you never chose runs on your server
An alert or a security advisory names a package nobody on the team remembers adding. Row 5 gives the reason: a flaw someone has already published in a package can stay reachable inside a product that works fine in every other way. In the same audits, the Dependencies & Supply Chain pillar averages 34.5 out of 100, scored on 20 of the 21 third-party apps. Two kinds of package trouble are separate subjects: npm malware packages, where the package itself is hostile, and how to detect Log4j vulnerability, where a widely used library carries a known hole. The first check is the npm audit command, control 5.
Someone found the hole and had nowhere to report it
An email from a stranger describing a flaw in your app lands in a support inbox nobody reads, and weeks pass. Row 9 says what was missing: the person who finds a flaw needs a plain route to someone who can act on it. If the flaw was used before anyone read that email, what to do next is covered in my app got hacked. The route itself is a security.txt file and a monitored inbox, control 9.
The 12 controls, one by one
Each control below says what it is in plain words, then the test that proves it, which is how to secure web applications in a form you can check. I would apply these application hardening best practices to any generated app, one control at a time.
1. Every write endpoint validates on the server
Every route or server action that writes data checks the incoming payload against an explicit schema, handles what comes in and goes out safely, and has defenses against injection. The test: submit invalid and hostile payloads and confirm safe rejection or handling. Inputs that name a file or a path deserve extra care, because a path built from input can pull in code or files the app never meant to serve; that case is remote file inclusion. The deep version of this control is the input validation page linked in the table.
2. File uploads are typed, sized and private
Uploads are checked for file type and size, the storage bucket’s permissions are enforced, and a stored file can be fetched only by someone allowed to see it. The check: test disallowed formats, oversized files, unauthorized downloads, and storage listings. A listing of the bucket that comes back full when you are logged out is the finding to look for first. The file upload testing page walks through each case.
3. Cross-origin access is restricted
The app answers cross-origin requests only from the origins it means to serve, and requests that carry cookies or tokens are checked together with login and request-forgery protection. The check: test approved and unapproved origins, credential handling, and state-changing request protections. An origin setting that echoes back whichever site asked, with credentials allowed, is the pattern to look for first. The forgery half is a separate subject: CSRF mitigation.
4. Production responses carry security headers
The production site sends a Content Security Policy, HSTS, framing restrictions and whichever other browser security headers are appropriate for the app, and each one has been tried against the live flows. The test: inspect response headers and exercise the application to confirm intended protections without broken flows. HSTS is aimed at the man-in-the-middle attack, where someone between the browser and the server reads or changes the traffic. The content security policy unsafe-inline page, linked in the table, covers CSP in depth.
5. Vulnerable dependencies are fixed, not ignored
Every dependency is audited, packages with known flaws are upgraded or taken out, and packages the app no longer uses are removed as well. The test: record the dependency scan, remediation, and regression checks after changes. Picking a scanner and sorting its output is the subject of vulnerability management tools. For a Node app, start with the npm audit command page.
6. Public endpoints are rate-limited
Each public form, signup flow and endpoint that costs money or compute gets an appropriate limit. The test: simulate bursts and verify enforcement without blocking ordinary use. The second half of that test matters as much as the first: a limit that blocks a whole office of real users behind one address is a new outage. Rate limiting in API routes, linked in the table, shows where the limit sits in a generated API.
7. The OWASP Top 10 has been walked, category by category
The app is reviewed against the OWASP Top 10, and each category gets its findings, fixes and evidence written down. The current edition is the 2025 list, whose first category is A01:2025 Broken Access Control and whose last is A10:2025 Mishandling of Exceptional Conditions. The test is the record: category-level results and supporting test evidence, with justified non-applicable cases identified. Classes that sit between the categories are covered in race condition in cyber security and the other classes a builder leaves, and the organization behind the list is explained in what is OWASP. The walk itself is on the page about how to test OWASP Top 10 vulnerabilities.
8. Public forms reject bots and accept people
Every public form carries a bot check, whether that is Turnstile, hCaptcha, or another appropriate option. Cloudflare Turnstile’s plans page lists a Free plan with unlimited challenges, and hCaptcha’s pricing page lists a plan called Basic (Free). The test: confirm valid users can submit and rejected or missing bot challenges are handled safely. Include the case with no challenge token at all: that post should fail on the server, not only in the widget. The setup itself is a separate subject, named in row 8 of the table.
9. There is a published way to report a vulnerability
The site publishes a security.txt file that points to a disclosure contact someone actually watches. The test: check the published file, contact details, and delivery of a test message. The test message matters most, since it is the only part that proves a report would reach a person. The security.txt example page has a file you can copy.
10. Five surfaces have had a targeted external test
The five externally reachable surfaces with the highest risk are tested from outside, what the test finds is fixed, and the methods and evidence are handed over. The test starts from one line: “Record the five surfaces, authorized tests, findings, fixes, and retest outcomes.” If the first question is what is the pen test and whether you need one, start there. The page on pen test vs vulnerability assessment separates the two requests, penetration test report format shows what the evidence should look like, and PCI DSS penetration testing covers the card-payment case.
11. The AI feature is hardened: injection, spend, outage
Each feature that calls a model defends against prompt injection, checks what the model returns before it writes data or reaches a user, caps each user’s usage, and times out to a fallback when the provider fails. The OWASP Top 10 for LLM Applications lists this risk first, as LLM01:2026 Prompt Injection in the 2026 edition OWASP published on August 3, 2026; it was LLM01:2025 in the edition before. The test: run injection test cases against each feature, a spend simulation that hits the limit, and a provider-outage simulation in staging. The what is prompt injection page explains the attack.
12. Something absorbs floods before the origin
A layer sits between the internet and the app server, a CDN or a web application firewall, with rules for rate and abuse that soak up a flood before the app ever sees it. On Vercel, automated DDoS mitigation covers every deployment on every plan, Hobby allows up to 3 WAF custom rules and Pro up to 40, and managed rulesets are listed as not available on either plan. The test: send a load burst at the edge and confirm it is absorbed while the origin’s health endpoint stays green. Other hosts are a separate subject, named in row 12 of the table.
Scans, audits, reviews and pen tests: which page answers which request
A scan, a security review, an audit, a pen test and a threat model are different requests, and each has its own page in this area. When someone asks for a security check, the first job is to find out which of these they mean; the table below sends each request to its page.
What each kind of check proves, and who accepts it as evidence, is already laid out in which security check you are actually being asked for, and the code-audit angle is in the code audit service article. This table only routes the request.
| What you are asked for | The page that answers it |
|---|---|
| A scanner run on the site or the code | website security check, static code analysis tools, SAST vs DAST |
| A security review of the code | what a security review checks |
| A web application security audit | web application security audit |
| An application security checklist, filled in | application security checklist, SaaS security checklist, vibe code security check |
| An API security assessment | API security assessment |
| A cloud application security assessment | cloud application security assessment |
| A threat model | threat modeling a small web app |
| A pen test | the targeted external test control above, and the web application penetration testing page linked in the first table |
If the request is to pick an appsec tool, the first row covers the kinds of scanner and what each one reads. Mobile changes the list; mobile app security best practices says how. An appsec program for a small team, in my reading, is this table plus the 12 controls run on a schedule.
The principles the 12 checks come from
Four security principles sit behind the 12 controls: least privilege, complete mediation, defense in depth, and the rule that hiding something is not protecting it. The controls in the table apply them where the app takes input or serves output.
Of the cyber security principles, these four are the ones that show up in how a single app handles a request; other principles of information security are about people, policy and paperwork, and sit outside this page. In cyber security, security controls are the safeguards that put a principle into practice, and the 12 in the table are that kind.
Least privilege, in NIST’s glossary words, is “A security principle that a system should restrict the access privileges of users (or processes acting on behalf of users) to the minimum necessary to accomplish assigned tasks.” Control 3 applies it to origins, which get access only if the app needs them, and control 11 applies it to model usage, which is capped per user.
Complete mediation, as cyber security uses the term, means every request is checked, every time, including requests that never went through the app’s own screens. Control 1 applies it to every write.
Defense in depth means layers, so one failure is not the whole story: headers in the browser (control 4), limits in the app (control 6) and a filter at the edge (control 12). Defense in depth as a principle goes further than these three.
Security through obscurity is the fourth idea, and it fails as a control. An endpoint nobody linked to is still an endpoint, and a script that lists routes finds it the same way a curious user would.
Confidentiality, integrity and availability, for your data
Confidentiality means only the right people can read the data, integrity means it changes only through the right path, and availability means it is there when asked. For one app, integrity is a validated write by an authorized caller, plus a backup that can be restored.
The integrity definition in cyber security that NIST’s glossary gives is “Guarding against improper information modification or destruction, and includes ensuring information nonrepudiation and authenticity.” In plain words, that is what integrity means in cybersecurity: data changes only in ways that are allowed, and you can tell who changed it. NIST’s definition of integrity lists several more wordings from other NIST publications.
For one app, the integrity of information rests on the integrity of the system that stores it. A row should change only through a validated write (control 1) by a caller the app has authenticated and authorized, which is what the authentication checklist covers, and the proof that a bad change can be undone is a backup that restores, one of the database checks before launch.
The words in a security report
A security vulnerability report uses a small vocabulary, and each word asks for a different action. Security vulnerability examples from this page include an endpoint that accepts hostile input, an origin setting that trusts every site and a package with a published advisory.
| Term | What it means | What to do when you see it |
|---|---|---|
| Vulnerability | A weakness an attacker could use to make the app do something it should not | Ask whether it is reachable in your app, then fix it or write down why not |
| Security flaw | A mistake in design or code that creates a weakness | Fix the code path, then check where else the same pattern was generated |
| CVE | The public list NIST’s glossary describes as “A list of entries-each containing an identification number, a description, and at least one public reference-for publicly known CS vulnerabilities”; a CVE ID names one entry | Look up the entry and check whether the version you run is affected |
| Attack vector | In cyber security, the route an attacker takes to reach a weakness: a form, an API route, an upload | Close the route or put a limit on it |
| Exploit | Code or steps that put a vulnerability to use | Move a finding with a public exploit to the top of the list |
| False positive | A finding flagged as a cyber security problem that is not a real problem in your app | Record the reason, so the next scan’s reader does not recheck it |
| Severity | The rating a report gives a finding | Weigh it against what the data behind the finding is worth to you |
| Security measure (control) | Anything that blocks or limits an attack: a validation rule, a header, a limit | Match it to the finding it closes |
The CVE wording above is NIST’s glossary entry, sourced from MITRE; the list itself is run by the CVE program. A CVE ID has a fixed meaning in security: it points to a public record about a package or product, and says nothing yet about whether your app can reach the flaw. How a finished report lays these words out is the penetration test report format page, and sorting a long scanner list is the vulnerability management tools page, both linked in the controls above.
Per-stack notes: the server, the framework and the language
Secure web servers matter most when you run one yourself: on a managed host the platform runs the server, and you still own the headers and the limits. On a VPS, web server security is a separate job, and Apache web server hardening, like the nginx version, belongs to the deployment area linked in the table below. The column of controls to check first is my reading of where to start on each stack; the built-in column quotes each project’s own docs where I read them.
| Stack | Controls to check first | The built-in that helps |
|---|---|---|
| Managed host (Vercel, Netlify, Railway) | 4 headers, 6 limits, 12 edge | Vercel’s platform firewall; the plan detail is under control 12 |
| nginx or Apache on a VPS | 4 headers, 12 edge, then the server itself | Server settings, covered in environments, CI/CD and deployment |
| Next.js and React | 1 validation, 3 origins, 4 headers | Next.js’s data security guide: “You should always validate input from client, as they can be easily modified.” |
| Node and Express | 1 validation, 3 origins, 6 limits | Express’s security best practices page recommends Helmet, “a middleware function that sets security-related HTTP response headers” |
| Python (Django, Flask) | 1 validation, 3 origins, 4 headers | Django: “The CSRF middleware is activated by default in the MIDDLEWARE setting.” Flask’s security page: “Flask configures Jinja to automatically escape all values unless explicitly told otherwise.” |
| PHP | 1 validation, including raw query strings and file includes, then 7 | Not researched for this page |
| Java and ASP.NET | 1 validation, 4 headers, 5 dependencies | Not researched for this page |
| Ruby on Rails | 1 validation, 4 headers | ”Both Action View and Action Text build their sanitization helpers on top of the rails-html-sanitizer gem.” |
On Next.js, the same guide adds that Server Actions should be treated as “reachable via direct POST requests” with authentication and authorization checked inside each one, which is control 1 and the access checks together; Next.js’s data security guide has the examples. On the JavaScript side, the vulnerabilities that matter most here sit in the Node API, in my reading, and keeping JavaScript secure there means controls 1, 3 and 6. Node.js hacking, seen from the defending side, goes after the same three: routes that trust their input, origins that are too open and endpoints with no limit.
For secure Python, Django and Flask start from different places: Django turns its CSRF middleware on by default, as the how-to guide quoted in the table says, and Django’s CSRF protection reference explains how that protection works. A secure Flask app has to add CSRF itself, because Flask’s security considerations say the form validation framework that would do it “does not exist in Flask.”
PHP, Java and ASP.NET have no built-in in the table because I did not research their defaults for this page. For PHP security, the vulnerabilities to check first are raw query strings that reach the database and file includes built from input, both under control 1; to secure PHP code beyond that, the OWASP Top 10 categories apply as they stand. For a Java web app or an ASP.NET one, security best practices follow the same order as for any web application: validation at the API boundary first, then headers and dependencies.
The Rails HTML sanitizer is the gem that both Action View and Action Text build their sanitization helpers on, and the Rails security guide’s advice for it is to “prefer permitted lists over restricted lists.”
How to verify the whole area in a day
Verifying this area takes 12 tests and, as my working estimate for a small app, about a working day: hostile payloads on each write, the upload tests, a cross-site request, the response headers, a dependency scan, a burst on the costliest endpoint, the OWASP walk, a form post without the bot check, and a test disclosure message.
Each item points back to its control above, where the test itself is written out; this list only sets the order a one-person team can run them in and what to keep as the record. The rest of the app belongs on the full production readiness checklist.
- 01 Hostile payloads on every write route (control 1). Keep each route and what it returned
- 02 The upload cases (control 2). Keep which files were refused and which downloads were blocked
- 03 An unapproved origin and a credentialed cross-site request (control 3). Keep the responses
- 04 The response headers on the production domain (control 4). Save the headers and note any flow that broke
- 05 The dependency scan and its fixes (control 5). Keep the scan output from before and after
- 06 A burst on the costliest endpoint (control 6), against staging or with the paid provider in its test mode, so a missing limit sends no real texts and spends no real credits; that is my working rule. Keep the point where the limit started refusing
- 07 The OWASP category walk (control 7). One line per category, with its evidence or the reason it does not apply
- 08 A form post without the bot check (control 8). Keep the refusal, then one normal submission that went through
- 09 A test message to the disclosure contact (control 9). Keep when it arrived and who read it
- 10 The five-surface record (control 10). Surfaces, tests, results, fixes and retests in one file
- 11 The AI feature's injection, spend and outage cases (control 11). Keep each case and its result
- 12 A load burst at the edge with the health endpoint watched (control 12). Keep the burst size and the health checks during it
Where the sprint stops
The external test in control 10 is targeted at five surfaces, and AxonBuild performs this targeted test. Formal third-party certifications and independent audit opinions are separate from the sprint deliverables, so a contract that asks for an independent test needs an independent tester. The app’s current framework and hosting setup are the starting point, and components are refactored or replaced where the production work requires it. Hosting, paid tools and API usage remain in the client’s own accounts, and any required third-party costs are explained before they are enabled.
Where the sprint does this
Area 3 of the Production Hardening Sprint is these 12 controls, 12 of its 123 deliverables. Each is delivered as its section above describes and checked by the test quoted there. Every result goes into the production readiness report, which accounts for all 123 IDs, keeps failures visible until resolved and explains genuine non-applicable items. The full list is area 3 of the published scope.
Common questions about securing an app you did not write
What are the top 5 web application vulnerabilities?
By the current OWASP Top 10, the first five are Broken Access Control, Security Misconfiguration, Software Supply Chain Failures, Cryptographic Failures and Injection, in that order. OWASP ranks categories of weakness rather than single bugs, so each maps to controls: access checks belong to the authentication and authorization work outside these 12, misconfiguration to controls 3 and 4, the supply chain to control 5, cryptographic failures partly to the HSTS header in control 4, and injection to control 1.
What is the best hardening checklist?
For an app, the best hardening checklist in my view is one where every item has a test that proves it, which is what the 12-row table near the top of this page is. For a server or operating system, the CIS Benchmarks for the software you run are the reference to look at; that is server work, separate from the app’s.
How do you perform hardening?
Hardening is done in three moves: remove risky defaults, close inputs the app does not need, and add limits wherever one caller could do too much. Then prove each change with its test, in the order the one-day list above gives.
What is hardening and patching?
Hardening removes what an attacker can reach; patching applies a fix for a flaw that is already known. NIST’s glossary describes a patch as “the immediate solution to an identified problem that is provided to users”. For an app’s dependencies, control 5 is the patching half: upgrade or remove the packages with published flaws, then rerun the regression checks.
What are the security considerations for SaaS?
For the SaaS you built, the security considerations are the 12 controls on this page plus authentication and authorization, secrets and keys, and the database: who can see which customer’s data, where the keys live, and whether a backup restores. For SaaS you buy, security is a different job, done by posture management (SSPM) tools that watch the settings of other companies’ apps.
What is web application security?
Web application security is keeping a web app’s code, its requests and its data from being misused, from the browser through the API to the database. For one app, it comes down to the 12 controls in the first table, each proved by a test.
The checks in this guide show you where the app is open. The sprint below closes those gaps, tests the result and writes the evidence down.
Built it with AI. Now it has to hold up for real customers.
The Production Hardening Sprint takes the app you already have and builds the production foundation underneath it. Authentication and access rules, payments that stay consistent, error handling, monitoring, backups, automated tests and a documented handover. Our engineers work inside your existing codebase for ten working days. All 123 deliverables are included, and you get the evidence for each one.
See the Production Hardening Sprint →
$2,500 fixed price · 10 working days · One codebase