hooks.py, and install the app on a site. Frappe then generates the forms, list views, role permissions and REST endpoints for each DocType.What "from scratch" means in Frappe
Frappe is a full-stack, metadata-driven web framework written in Python and JavaScript. ERPNext is one application built on it. A custom Frappe app is another application on the same foundation, and it can run beside ERPNext on one site, depend on it, or stand alone.
“From scratch” therefore rarely means writing a login system, a database layer or an admin interface. Those come with the framework. What you write from scratch is the part specific to your business: the records you track, the rules between them, and the screens and integrations your team needs.
That distinction matters for scoping. A custom app is usually a thinner piece of work than the equivalent project on a general-purpose web stack, but it still needs the same discipline around design, testing and release.
The building blocks you will actually use
the command-line tool that manages Frappe installations. It creates the app skeleton (bench new-app), creates sites, installs apps on them and runs migrations.
a Python package with a standard layout, containing modules, DocTypes, pages, reports and configuration. One bench can hold several apps, and one site can install several of them.
the core unit. A DocType describes a record type through fields, validations and permissions. Frappe uses that description to create the database table, the form, the list view and the REST API.
a Python class tied to a DocType, where server-side logic lives, such as validation before save or actions after submit.
JavaScript that adjusts how a form behaves in the browser, for example showing a field only when another has a certain value.
the app's wiring file. Here you register event handlers on other apps' DocTypes, scheduled jobs, fixtures and assets without editing the other app's code.
access rules defined per DocType and role, with optional record-level restrictions.
A typical build, in order
List the records, who creates and approves them, and how they relate. Decisions here are the most expensive to reverse later.
Create the app and a development site, and keep the app in version control from the first commit.
Start with the few that carry the business process, and use child tables and link fields for relationships.
Put validations and state changes in Python on the server, where they cannot be bypassed from the browser.
only where the generated form falls short.
early, not at the end. Retrofitting access rules across many DocTypes is tedious.
Frappe runs patches during migration, so schema or data updates ship with the app instead of being done by hand per environment.
with the same bench workflow on staging and production.
If your team already writes Python, most of this will feel familiar. The unfamiliar part is usually the framework’s conventions, which take some time to absorb before the speed benefit shows up.
If you are weighing whether your team should take this on or bring in specialists, our Frappe framework development service page describes how we approach that kind of work.
Where custom Frappe apps go wrong
Editing ERPNext core instead of extending it
Changing standard code works until the next upgrade, then becomes a merge problem. Hooks, custom fields and a separate app exist so core stays untouched.
Treating DocTypes like spreadsheet columns
Adding fields freely is quick, but a model with no clear ownership of data becomes hard to report on and hard to permission.
Skipping patches and fixtures
If a change exists only on one developer's site, it will not be on production. Anything that must travel with the app should be captured in code.
Customizing what configuration already covers
ERPNext handles a lot through settings, custom fields and workflows. Writing code for something configuration could do adds maintenance for no gain.
Ignoring upgrade cost
Frappe and ERPNext release major versions, and a custom app must keep pace. This is an ongoing responsibility, not a one-time build.
Build a custom app or configure ERPNext?
| Situation | Usually better fit |
|---|---|
| A standard process with different fields or approvals | Configure ERPNext: custom fields, workflows, print formats |
| A standard module needs a few extra rules | A small custom app using hooks, leaving core untouched |
| A process that does not exist in ERPNext at all | A custom app with its own DocTypes |
| A product you plan to offer to many customers | A custom app, designed for installation and upgrades from day one |
The honest answer is often a mix: ERPNext for what it already does well, and a custom app for the part that makes your business different. If you want to see how that split works in practice, our custom Frappe apps service page lays out where we typically draw the line.
What to evaluate before committing
Does the application naturally revolve around records, approvals and reports? Frappe fits that shape well. It is a less natural fit for highly bespoke, consumer-facing interfaces, though Frappe also supports separate front ends through its API.
Who will maintain it after launch, and do they know the framework?
How does it need to connect to ERPNext, accounting, e-commerce or other systems?
What is the plan for major-version upgrades?
Frequently Asked Questions
A team with Python experience can learn it, and many do. The tradeoff is time to proficiency and the risk of early design mistakes. Some teams hire for the initial design and then maintain the app themselves. If that sounds useful, you can look at how to hire Frappe developers through us.
Next step
Frappe rewards a clear data model and punishes shortcuts around core code. If you are evaluating it for a custom application, the most useful first step is usually a short technical conversation about the process you want to support and what already exists in ERPNext.
Talk to our Frappe development team. Tell us what you are planning to build, and we will give you an honest view on whether Frappe is the right base and how we would approach it.






