Hourly (time and materials) seems the most common, but customers will still ask you estimate for tasks. If the task gets big you will feel tempted to make up a number that feels right and multiply by the hourly rate. That turns it into a big fixed-fee project. Which leads me to...
Big fixed-fee projects -- tens or hundreds of thousands of dollars and schedules that run for months -- open up too many opportunities for misunderstandings, and leave both parties exposed to owing or expecting amounts large enough to lead to lawsuits and arbitration. Instead I propose a more agile (in the original sense of the term) process, where the freelancer and the customer first break the project down into smaller pieces, in order, that have clear deliverables and price tags. That gives some advantages:
- More frequent review and feedback from the customer, which keeps expectations aligned.
- Less money at stake at any time for both sides.
- Customer sees constant and measurable progress, which will either make them feel better about the schedule and budget, or support discussions about a possibly unrealistic schedule and budget.
- Project has more velocity and more feedback loops. The customer develops trust in the developer for delivering, and the developer learns more about the business domain and the actual requirements.
I have seen many freelancing projects written up as fixed-fee that then go sideways. A couple of years ago a customer came to me after losing $65,000 on a web site rewrite that dragged out for eight months, and at the end what they got was not usable. Not a huge amount of money but that represented the customer's entire budget for the project. So we reviewed how that happened. The contract was typical: $65,000 total, half up-front to start, with vague requirements (a single page) and a schedule that seemed realistic before any work started. The developers staged exactly one demo site in eight months for review, got some feedback, then started billing for "change orders" based on their opinion that the customer was changing the requirements mid-project. Once the entire budget got spent the developers stopped work, demanding more money. Lawyers got involved, arbitration scheduled (mandatory in their state), that's still dragging on. No web site to show for it.
In hindsight it seems obvious the customer should have required the developers to maintain a staging site and push changes at least weekly, with a process for the customer to give feedback and change direction. If the project was broken out into iterations, with clear deliverables, the customer could have seen much sooner that the developer was off-track or not producing, and the developer would have known about requirements changes much earlier in the process. If they had decided to call it off at any time neither side would owe or expect tens of thousands of dollars.
Writing complete and unambiguous requirements for non-trivial software projects remains a holy grail of programming. Almost no experienced developers can do it, and in my experience customers can't even start -- they think in terms of how their business works but may not even know how to describe that in sufficient detail. Knowing this well-documented fact of software development that every professional should understand (since we've known about it since at least the 1960s), agile as described in The Agile Manifesto outlines an alternative approach based on frequent small releases, review and feedback, iteration, measurement. I think that approach has a higher probability of success, or at least lower probability of massive and contentious failure, than the usual price tag with vague specs as a one or two page exhibit on a boilerplate contract.
Focusing on deliverables forces both parties to define what they expect. Developers have a better chance of giving good binding estimates if they have clear specifications and bite-sized tasks with clearly-defined criteria for "done." The bigger and more vague the specs the more chance for misunderstanding, and those misunderstandings multiply and compound the longer the project goes on.
Programmers should expect requirements to change and details to emerge during development -- that is the norm when writing software, not an exception you submit a "change order" for. We're not assembling Legos -- we're writing new code from scratch and refining it until the customer agrees it's done.
"We need a new web site that lets our members list their talents in a directory. It should let new members join and pay online, choose their directory categories, upload photos and videos. We have several membership models and prices. We also want to show ads on the site." That was more or less the original spec in the contract for the project I described above. It had a little more detail but not much. The developers responded with mostly technical jargon: "AWS EC2 medium instance, Python/Django and Postgres back-end. Client will pay for all graphics, fonts, assets and provide full copy for web pages." I think it's obvious that combining the specs and the development plan, if you can even call it that, does not produce requirements anyone can estimate, schedule, or code from. It's just a recipe for conflict and disappointment.