— Artikel — № 054

054 —Business

Offerte voor legacy werk: een offertebrief in vier tiers

De offertebrief in vier tiers die we sturen voordat we aan een legacy site beginnen. Offreer de oppervlakte, omhein het moeras, en houd de klant eerlijk.

Bovenaanzicht van scope-brief in vier niveaus, offerte, manilamap, messing plaatje, liniaal, potlood en rode lakzegel.
Hero · gestileerd stilleven№ 054

Vorige maand kwam er een aanvraag voor legacy werk binnen van een Nederlands bureau dat een WooCommerce winkel uit 2014 had geërfd van een klant wiens vorige ontwikkelaar geen e-mail meer beantwoordde. De briefing was vier regels: "Zorg dat de checkout weer werkt. Voeg Mollie toe. Verhuis van de shared host. Offerte graag, eind van de week." Bijgevoegd zat een ZIP van de live wp-content/ directory, 1,8 GB, met een plugins/ map die 41 plugins bevatte, waarvan 9 nulled, en een mu-plugins bestand genaamd fix-checkout-final-v3.php gedateerd 2019.

Dit is het moment waarop je óf een getal op een servet schrijft en daar later spijt van krijgt, óf een offertebrief schrijft. Wij schrijven altijd de offertebrief. Offreren voor legacy werk zonder is de snelste manier om van een klus van €4.000 een verontschuldiging van €17.000 te maken, en van de klant iemand die anderen vertelt dat je over budget ging.

Wat volgt is de structuur van de brief die we daadwerkelijk versturen. Hij heeft vier tiers omdat legacy werk vier soorten onbekenden kent, en die als één bedrag offreren is precies wat je het moeras in jaagt.

Waarom één getal altijd verliest

De pathologie van offreren voor legacy werk is dat zichtbaar werk en onzichtbaar werk er van buiten identiek uitzien. "Voeg Mollie toe aan de checkout" is een klus van vier uur op een schone site en een klus van drie weken op een site waar de vorige ontwikkelaar woocommerce_payment_complete heeft overruled in een child theme dat ook wp_mail() opnieuw definieert. Je kunt niet zien welke je hebt totdat je al binnen bent.

Eén vaste prijs dwingt je om óf op te plussen voor het slechtste scenario (en de deal te verliezen aan iemand die dat niet deed) óf het beste scenario te offreren (en het verschil zelf op te eten). Een puur uurtarief jaagt de klant weg, want ze zijn eerder gebrand door uurtarieven en hun argwaan is terecht. De brief in vier tiers splitst het verschil: vast waar we eerlijk kunnen zijn, op nacalculatie waar dat echt niet kan, met expliciete drempels ertussen.

Tier 1: de audit, vast en goedkoop

De eerste tier is altijd een betaalde audit. Niet gratis, niet "we kijken er even naar", geen kennismakingsgesprek. Een echte factuur, meestal tussen €450 en €900 afhankelijk van de oppervlakte, betaalbaar voordat we de server aanraken. De oplevering is een geschreven rapport, drie tot zes pagina's, dat de klant vertelt wat hij feitelijk heeft.

De audit dekt minimaal: PHP versie en EOL status (de pagina met ondersteunde versies van php.net is de canonieke referentie), MySQL versie, CMS core versie en patch gap, lijst met plugins inclusief laatste update datum, eventuele nulled of verlaten plugins, server stack (Apache/nginx, mod_php of PHP-FPM), .htaccess eigenaardigheden, custom code in mu-plugins of de functions.php van het actieve theme, en alle duidelijke beveiligingsproblemen tegen de OWASP Top Ten.

De audit heeft een vaste prijs omdat we hem kunnen scopen. We weten hoe lang het duurt om naar een site te kijken (vrijwel altijd tussen twee en zes uur). De klant krijgt een document dat van hem is, dat hij in theorie naar een andere ontwikkelaar zou kunnen brengen. Dat laatste is belangrijk: het verlaagt het gevoel van risico bij ja zeggen, omdat ze niet tekenen voor een project, ze kopen een rapport.

Ongeveer één op de vier audits eindigt daar. De klant leest het, besluit dat het werk groter is dan gedacht, en gaat óf opnieuw budgetteren óf weglopen. Dat is een feature, geen bug. We hebben het project niet verloren; we werden betaald om geen slecht project aan te nemen.

Tier 2: de omheinde fix, vaste prijs

Tier 2 is het werk waarvan de audit heeft bewezen dat het begrensd is. Concreet, gescoped, testbaar. "Upgrade PHP van 7.2 naar 8.2, inclusief de drie plugin patches geïdentificeerd in sectie 4 van de audit." "Vervang de verlaten woocommerce-checkout-fields plugin door het equivalent in woocommerce-checkout-field-editor, met migratie van de zeven bestaande custom velden."

De regel voor wat Tier 2 in gaat: we hebben de code gelezen, we kennen de vorm van de fix, en we kunnen de bestanden noemen die we gaan aanraken. Kunnen we de bestanden niet noemen, dan is het geen Tier 2.

Dit is waar het meeste echte geld zit, en waar de klant het gevoel heeft een echte offerte te krijgen. De brief somt elk item op, de prijs en de acceptatiecriteria. Die acceptatiecriteria zijn het ongracieuze deel dat je later redt. "De PHP 8.2 upgrade is af wanneer: de site laadt zonder fatal errors op staging, de checkout flow rondt een testbestelling end-to-end af, en wp-cli core verify-checksums geeft een schone uitslag." Als de klant dat ondertekent, kan hij je later niet vertellen dat het ook nog sneller had gemoeten.

Tier 3: het moeras, nacalculatie met een plafond

Tier 3 is het moeras. Het werk waarvan we weten dat het bestaat maar dat we niet kunnen prijzen, omdat de onbekenden onbekend zijn. Klassieke Tier 3 items: "het versturen van e-mail faalt soms op dinsdag", "de admin is traag", "er is een duplicate-order bug die we niet kunnen reproduceren".

Deze factureren we per uur, in eenheden van 30 minuten, tegen een schriftelijk plafond. Het plafond is het belangrijkste woord in de brief. "We schatten 6 tot 10 uur. We stoppen bij 12 uur en vragen om overleg voordat we doorgaan." Het plafond beschermt de klant tegen een onderzoek dat op hol slaat, en het beschermt ons tegen het moeten opeten van het verschil, of een ongemakkelijke mail moeten sturen bij uur 14.

De bewoording die we gebruiken, min of meer letterlijk:

Tier 3 items worden gefactureerd op €95/uur in eenheden
van 30 minuten. Onze beste schatting staat per item vermeld.
We overschrijden 150% van de schatting niet zonder schriftelijk
akkoord. Raken we dat plafond, dan stoppen we, schrijven we
op wat we hebben gevonden, en bepaal jij of we doorgaan,
pauzeren, of het item sluiten.

Klanten maken hier vrijwel nooit bezwaar tegen zodra ze het op papier zien. Het bezwaar dat ze hebben tegen uurfacturering is verlies van controle. Het plafond geeft die controle terug.

Tier 4: de uitgestelde lijst

Tier 4 is alles wat de audit aan het licht bracht waar de klant niet om gevraagd heeft en wat niet blokkerend is. Verouderde jQuery in het theme. Een wp-config.php met DB_PASSWORD in plaintext en het bestand wereld-leesbaar. Een .htaccess met een hotlink-protectie blok uit 2017 dat nu ook de nieuwe CDN blokkeert:

RewriteCond %{HTTP_REFERER} !^$
RewriteCond %{HTTP_REFERER} !^http(s)?://(www\.)?oldclient\.nl [NC]
RewriteRule \.(jpg|jpeg|png|gif)$ - [F]

We sommen deze op met een beschrijving van één regel, een ernstgraad (info / should / must), en geen prijs. Het doel van Tier 4 is niet upsellen. Het is op de plaat krijgen dat we het probleem hebben gezien en benoemd. Zes maanden later, als de CDN rekening explodeert of de database lekt, kan niemand zeggen dat we het hadden moeten zien. We zagen het, en hier is de mail.

Ongeveer de helft van de Tier 4 items wordt in het werk van het volgende kwartaal getrokken. De andere helft blijft daar voor altijd staan, wat ook prima is.

De begeleidende brief die het werk eigenlijk verkoopt

De vier tiers vormen het lichaam van de brief, maar de begeleidende brief is wat de deal sluit. Twee alinea's. De eerste vertelt in gewone taal wat we hebben gevonden: "Je site draait op PHP 7.2, dat sinds november 2020 geen beveiligingspatches meer krijgt. Drie van je plugins zijn sinds 2018 niet meer geüpdatet. De checkout faalt op dit moment voor klanten die iCloud Hide My Email gebruiken vanwege een regex in jullie custom validatie."

De tweede alinea vertelt wat we aanbevelen om eerst te doen, en waarom. Niet alles. De kleinste hap die de klant uit acuut gevaar haalt. Meestal Tier 1 plus één of twee Tier 2 items. De rest kan wachten op de volgende offerte.

Hoe dit eruitziet als we Pier inzetten op dezelfde klus

Toen we Pier bouwden liepen we vanaf de andere kant tegen precies dit patroon aan. De audit fase is grotendeels lezen: rondsnuffelen in wp-content/, wp-config.php openen, door .htaccess scrollen, een paar read-only queries draaien tegen de database. We zijn uiteindelijk uitgekomen op de hele audit in één app laten plaatsvinden, met version history op elk bestand dat we aanraken en een MySQL editor naast de bestandsboom, zodat een audit van vier uur ook echt vier uur blijft in plaats van een yak-shave door drie SFTP clients en Sequel Pro.

Het kleinste wat je vandaag kunt doen: pak de volgende offerte-aanvraag voor legacy werk in je inbox en, voordat je antwoordt met een bedrag, reageer met alleen de prijs van de audit. Kijk wat er gebeurt.

— Vragen —

Wat als de klant weigert te betalen voor een Tier 1 audit?

Dan is het geen klant voor legacy werk. De audit fee is het goedkoopste filter dat je hebt. Klanten die weigeren te betalen zijn dezelfde klanten die later de eindfactuur gaan betwisten.

Hoe lang moet de offertebrief zijn?

Twee tot vier pagina's. Begeleidende brief, de vier tiers als tabel, acceptatiecriteria voor Tier 2, de plafondclausule voor Tier 3, en de Tier 4 lijst. Alles wat langer is wordt niet meer gelezen.

Onderteken je een NDA voor de audit?

Ja, en we sturen onze eigen NDA van één pagina mee met de audit factuur. Het sjabloon van de klant gebruiken voegt meestal twee weken juridische review toe zonder dat het wat oplevert.