Every freelance developer, agency or consultant has lost money on a project because of a bad estimate: too low out of fear of losing the client, too high without being able to justify it, or just plain wrong because the hours were guessed on gut feeling without a real process behind them.
I've done it for years, and every time I told myself "the next one is going to be more accurate". But it kept happening: I was usually short on hours, ending up working extra for no money because I was lacking a real method to calculate the amount of work needed to complete the project.
So I decided that "more accurate" needed an actual definition.
The problem isn't the lack of methods. Already in University, I studied academic models for estimating software costs that were introduced since the 1970s: COCOMO, Function Points, the observations in the Mythical Man-Month. They're a great foundation to have a specific view, but they're almost never good to use on their own and nobody actually uses them in practice because they were designed for large organizations estimating multi-year, multi-team projects, way before todays technological landscape. Translating them into a solo developer or small agency use case is usually a waste of time, usally producing a cost that's not reflecting the reality.
So I built Project Estimator: a free, local-only tool that condenses these models into a single equation that lets you define all the parameters to populate the mathematical input in a few minutes.
The core equation
By starting from the concept of a Work Item (the actual stuff you have to do) I mapped it to a tree. Every Work Item is a leaf node and parents just aggregate their children in sub-groups of Work Items.
E.g. a front-end integration might require writing the visual component, a library to process inputs and another to talk to a back-end system. These are all Work Items that can be grouped under the "Front-end integration" sub-group.
Anyway, the real estimation happens at the leaf level, so this can really be viewed as a bottom-up approach from a calculation standpoint.
The equation is:
Nominal = Base × Deliverable × Complexity × SkillGap × Kept + IntegrationHours
Each factor is a separate correction to a single human input: baseHours, which is the number of hours the work would take under ideal conditions: a competent developer, without suprises, doing the job from scratch. Everything else adjusts around this anchor.
Deliverable scales upward based on the required quality level, from prototype (no multiplier) to mission_critical (maximum). The same feature can be amplified in amount of hours according to the amount of care needed to deploy it. A throwaway feature takes a fraction of the time it takes when it needs observability, correct integrations, redundancy, test-coverage and zero tolerance for failure.
Complexity collapses 8 axis into a single multiplier using a weighted sum with dumping:
complexity = 1 + (Σ weight_i × (scale_i - 1) / 4) × damping
The axis are:
- Algorithmic
- Infrastructure
- Integration
- UX
- Secrity
- Legal
- Fiscal
- Performance
The damping factor is what keeps the multiplier from exploding: many axes at moderate complexity weigh more than a single axis at maximum, but the result stays bounded and well estimated.
This is closer to how real projects work. You don't worry about one big thing at a time, but of several medium ones that compound to the total value of the work you're doing.
Skill gap takes only the worst gap across all required skills, not the average:
skillGap = 1 + coefficient × max(required - available, 0)
The rationale comes directly from The Mythical Man-Month: the skill you're missing the most sets the pace. Being overqualified on other axes doesn't compensate. Experience counts, and the ability to deliver production-grade products from the first moment is something a client should always take into consideration as a serious advantage on choosing a professional over the others.
Kept is the only factor below 1. It combines reuse (your own code reused) and offloading (work delegated to an external system) independently:
kept = (1 - reuseAvoided) × (1 - offloadAvoided)
The independence is intentional: reusing your own component AND delegating part of the work to an external system doesn't erase the effort entirely. This is also the exact reason why the integration hours parameter exists and is added outside the final product. The cost of making an external system talk to yours is fixed regardless of how complex or simpe the work item is.
Risk is not work
This is always something we tend to underestimate: the risk of things not going well. I'm not talking about "the production database will explode". I'm talking about stuff like "the customer now wants a new modification because he changed his mind once the first version came out". This is a too much usual pattern. We, as professionals, tend to think that the customer already knows what will come out.
A real example: "I need to build a landing page with a contact form. Super easy". I built it, all good. Once the system hit production, the customer was unsatisfied with the amount of emails he was receiveing and the lack of a structured interface where he could see them all without using the email client. As a technical person, I was thinking "Ok, that's your own space, with a separate email only for this landing page, you'll read the email and everything will be fine". No, the customer wanted an UI to read the emails received. This is the kind of things that you can only take into consideration when you address risks separately from the work item itself, because it's not necessarily coming up and the fix is usually a workaround. But if no workaround is possible, the work item now drastically increases the amount of work, potentially conditioning other work items as well.
That's why risks live in a separate model. A risk is an uncertain event, not a task, and mixing the two produces estimates that are either bloated (if every risk always materializes) or dishonest (if risks are ignored entirely).
Each risk produces hours based on residual probability after mitigation:
riskHours = residualFraction × impactHours + mitigationHours
Mitigation hours are the amount of hours needed to mitigate the risk, and they're always paid because if you count risks too, you will spend time on making the adjustments regardless. Anyway, I decided to keep risks optional, with a toggle for "includeInEstimate", that decides if the final cost considers risks too or if they will be billed apart. The decision is yours.
Strategy movers the price, not the hours
Focusing on the hours itself is not enough: there is value in the work we do, but also in who we do it for.
The 9 strategy axes produce a single price multiplier, based on these parameters:
- Trust
- Client competence
- Communication ease
- Success probability
- Expected budget
- Portfolio interest
- Commercial interest
- Future work probability
- Webround Reuse (you can imagine it to be useful for any other platform)
The center is 3 and is a neutral value, which means the parameter doesn't condition the price at all.
The key decision here is that strategy is an added value to the project that shouldn't touch hours. The work is the work. The relationship with the customer is a whole other thing: by keeping them separate, the estimate is more honest.
The pricing chain
totalHours = nominalHours + riskHours
subtotal = totalHours × hourlyRate
after = subtotal × strategyMultiplier × (1 - discount) × (1 + margin)
The order is deliberate: strategy acts on the subtotal because it's a project-level judgment, then the commercial discount and finally, the margin. They're different parameters meant for different considerations.
What I got
After numerous works with different clients that ended up draining a lot of energy because of low estimates, I studied a series of different cost models to better price my works. After a while, I had an idea of what every project should have costed, because at the end of the work, I actually knew the hours spent on it. So, I tweaked the model until I had exactly what I had to gain from every single project. Estimates came out perfectly aligned with what I had to actually bill the client.
Today, I never state any price unless it's for a fixed price product. All the rest that involves my specific hands, mind, time and intuition must go through this mathematical model. It makes me sure I don't make mistakes.
Try it
The tool is totally free, with no sign-up and no back-end: your data is never recorded anywhere. This means you can't keep the estimates with you everywhere, but they're stuck on your browser until you decide to delete local data. All previous estimates are saved as JSON files in IndexedDB, but you can easily export the PDF to send it to the customer with a detailed plan of how much each work costs. You can also export the JSON file to reuse it later, or just save it in the browser.
Another useful thing is the bottom right button that lets you copy a prompt for LLMs. You can easily describe the project to the LLM and get a JSON output with all the pre-compiled details on the work item, so that you can later adjust everything according to your vision, skills and way of work.
Here's the link: project-estimator.webround.net
Built with Webround: an API-first e-commerce and website platform that supports React code directly from the browser.
Top comments (0)