Innlegg
Kodifisering av den hemmelige sausen
16. september 2019 • 5 min lesing
Hver applikasjon har sin hemmelige saus, sin grunn til å eksistere. Kodifisering av den hemmelige sausen er avgjørende for å skrive vedlikeholdbare og vellykkede applikasjoner.
Vent. Hva er kodifisering? Tålmodighet min venn, vi skal komme dit.
La oss først lage en hypotese:
Du har nettopp blitt forfremmet til Lead Software Engineer (Gratulerer!). Din administrerende direktørs første oppgave er å lage et nytt produkt for selskapet. Det er en regnskapsapplikasjon fra bunnen av. Ledelsen mener at en tilpasset regnskapsløsning vil gi dem en fordel over konkurrentene.
Noen måneder har gått, og de fleste av de tverrgående bekymringene er utviklet (hurra for deg!). Teamet fokuserer nå på det deilige ved applikasjonen: forretningsdomenet (den hemmelige sausen). Det er her kodifiseringen av den hemmelige sausen begynner.
Kodifisering er å legge struktur rundt et essensielt konsept i forretningsdomenet.
Innen regnskap er pris (P) inntjening (E) forhold (P/E-forhold) en måling av inntjening for et selskap. Et høyt P/E-forhold antyder høy inntjeningsvekst i fremtiden. P/E-forholdet beregnes ved å ta markedsverdien per aksje (aksjekurs) delt på inntjening per aksje (fortjeneste – utbytte / antall utestående aksjer).
En enkel, og jeg vil hevde, naiv implementering:
public class Metric
{
public string Name { get; set; }
public decimal Value {get; set}
public int Order {get; set;}
}
public class AccountingSummary
{
public Metric[] GetMetrics(decimal price, decimal earnings)
{
var priceEarningsRatio = price/earnings;
var priceEarningsRatioMetric = new Metric
{
Name = "P/E Ratio",
Value = priceEarningsRatio,
Order = 0
}
return new [] {priceEarningsRatioMetric};
}
}
Hvis dette bare brukes ett sted, er det greit. Hva hvis du bruker P/E-forholdet på andre områder?
Som her i PriceEarnings.cs
var priceEarningsRatio = price/earnings;
Og her i AccountSummary.cs
var priceEarningsRatio = price/earnings;
Og her i StockSummary.cs
var priceEarningsRatio = price/earnings;
P/E-forholdet er kjernestoff i denne applikasjonen, men hvordan det implementeres, hardkodet på ulike steder, gjør at viktigheten av P/E-forholdet går tapt i et hav av kode. Det er bare et annet tre i skogen.
Du åpner også for risikoen for å endre forholdet på ett sted, men ikke på det andre. Dette kan påvirke nedstrømsberegninger. Denne typen feil er notorisk vanskelige å finne.
Ofte vil testere anta at hvis det fungerer på ett område, er det korrekt på alle områder. Hvorfor skulle ikke applikasjonen bruke samme kode til å generere P/E-forholdet for hele applikasjonen? Er ikke dette poenget med objektorientert programmering?
Jeg kan forestille meg en feil som dette som kommer inn i produksjon og ikke blir oppdaget før et besøk fra din administrerende direktør som krever å vite hvorfor SECs P/E-forholdsberegninger er forskjellige fra det selskapet sendte inn. Det er ikke et bra sted å være.
La oss se på vår P/E-forholdsimplementering på nytt og se hvordan vi kan forbedre vårt første forsøk.
I regnskapssystemer er formler en ting, la oss legge struktur rundt formler ved å legge til et grensesnitt:
public interface IFormula
{
decimal Calculate<T>(T model);
}
Hver formel implementeres nå med dette grensesnittet, noe som gir oss konsistens og forutsigbarhet.
Her er vårt forbedrede P/E-forhold etter implementering av vårt grensesnitt:
Vi har lagt til en PriceEarningsModel for å sende de nødvendige dataene til vår Calculate-metode.
public class PriceEarningsModel
{
public decimal Price {get; set;}
public decimal Earnings {get; set;}
}
Ved å bruke PriceEarningsModel, har vi laget en implementering av IFormula-grensesnittet for P/E-forholdet.
public class PriceEarningsRatioFormula : IFormula
{
public decimal Calculate<PriceEarningsModel>(PriceEarningsModel model)
{
return model.Price / model.Earnings;
}
}
Vi har nå kodifisert P/E-forholdet. Det er et førsteklasses konsept i vår applikasjon. Vi kan bruke det hvor som helst. Det er testbart, og en endring påvirker hele applikasjonen.
Som en påminnelse, her er implementeringen vi startet med:
public class Metric
{
public string Name { get; set; }
public decimal Value {get; set}
public int Order {get; set;}
}
public class AccountingSummary
{
public Metric[] GetMetrics(decimal price, decimal earnings)
{
var priceEarningsRatio = price/earnings;
var priceEarningsRatioMetric = new Metric
{
Name = "P/E Ratio",
Value = priceEarningsRatio,
Order = 0
}
return new [] {priceEarningsRatioMetric};
}
}
Det er enkelt og gjør jobben. Problemet er at P/E-forholdet, som er et kjernekonsept i vår regnskapsapplikasjon, ikke skiller seg ut. Ingeniører som ikke er kjent med applikasjonen eller forretningsdomenet, vil ikke forstå dens betydning.
Vår forbedrede implementering bruker vår nye P/E-forholdsklasse. Vi injiserer PriceEarningsRatioFormula-klassen inn i vår AccountSummary-klasse.
Vi erstatter vårt hardkodede P/E-forhold med vår nye PriceEarningsRatioFormula-klasse.
public class AccountingSummary
{
private PriceEarningsRatioFormula _peRatio;
public AccountingSummary(PriceEarningsRatioFormula peRatio)
{
_peRatio = peRatio;
}
public Metric[] GetMetrics(decimal price, decimal earnings)
{
var priceEarningsRatio = _peRatio.Calculate(new PriceEarningsModel
{
Price = price,
Earnings = earnings
});
var priceEarningsRatioMetric = new Metric
{
Name = "P/E Ratio",
Value = priceEarningsRatio,
Order = 0
}
return new [] {priceEarningsRatioMetric};
}
}
Man kunne hevde at det er litt mer arbeid med PriceEarningsRationFormula sammenlignet med den forrige implementeringen, og jeg ville være enig. Det er litt mer seremoni, men fordelene er godt verdt den lille økningen i kode og seremoni.
For det første får vi muligheten til å endre P/E-forholdet for hele applikasjonen. Vi har også en enkelt implementering å debugge hvis det oppstår defekter.
Til slutt har vi kodifisert konseptet PriceEarningsRatioFormula i applikasjonen. Når en ny ingeniør blir med i teamet, vil de vite at formler er essensielle for applikasjonen og forretningsdomenet.
Det finnes andre metoder for kodifisering (innkapsling) av domenet, for eksempel mikrotjenester og samlinger. Hver tilnærming har sine fordeler og ulemper, og du og ditt team må bestemme hva som er best for applikasjonen din.
Å plassere viktige domenekonsepter i klasser og grensesnitt skaper gjenbrukbare komponenter og konseptuelle grenser. Dette gjør en applikasjon lettere å resonnere med, reduserer defekter og senker barrierene for å få nye ingeniører om bord.
Forfatter: Chuck Conway er en AI-ingeniør med nesten 30 års erfaring innen programvareutvikling. Han bygger praktiske AI-systemer—innholdspipelines, infrastrukturagenter og verktøy som løser virkelige problemer—og deler det han lærer underveis. Koble til ham på sosiale medier: X (@chuckconway) eller besøk ham på YouTube og på SubStack.