Enhver ændring af et produktionssystem medfører konsekvenser, der rækker ud over den ændrede komponent. En ændring af en delt funktion afbryder kaldere, der var afhængige af dens tidligere adfærd. En ændring af databaseskemaet ugyldiggør lydløst enhver forespørgsel, der refererede til den ændrede kolonne. En COBOL-kopibogopdatering kræver, at alle programmer, der inkluderer den, genkompileres, et omfang, der kan strække sig over hundredvis af programmer på tværs af snesevis af jobstrømme, som alle skal testes før enhver produktionsoverførsel. Spørgsmålet, som konsekvensanalyse besvarer, er ikke, om en ændring har konsekvenser, men præcis hvilke komponenter der påvirkes, hvordan de er forbundet med det ændrede element, og hvad det fulde omfang af validering skal være, før ændringen er sikker at implementere.
Opdag synkroniseringsfejl, før brugerne gør det
SMART TS XL kortlægger alle dataforhold, så dit team sporer kvalitetsfejl, før de når søgeresultaterne.
Lær mereUden konsekvensanalyse besvares dette spørgsmål ved at gætte, ved at spørge udvikleren, der har foretaget ændringen, ved at køre hele testsuiten og håbe, at fejlene grupperer sig omkring de rigtige ting, eller ved at implementere og opdage de berørte komponenter, når brugerne rapporterer fejl. Konsekvensanalyseværktøjer erstatter gætværk med strukturelle beviser: de analyserer kildekode, kortlægger afhængigheder og genererer en opregnet liste over alle komponenter, som den foreslåede ændring vil påvirke. De værktøjer, der er dækket i denne vejledning, spænder fra statiske analyseplatforme til testudvælgelsesmotorer til virksomhedsafhængighedskortlæggere, der hver især dækker en forskellig dimension af konsekvensanalyseproblemet.
Hvad er konsekvensanalyse i softwareudvikling?
Konsekvensanalyse inden for softwareudvikling er processen med at identificere alle komponenter i et system, der er direkte eller indirekte påvirket af en foreslået ændring. Den besvarer: hvis jeg ændrer dette, hvad ændres der så ellers? Den fungerer før implementering, under planlægning, design og godkendelse af ændringer, snarere end efterfølgende under test eller hændelsesrespons.
Begrebet dækker over flere relaterede, men forskellige aktiviteter, der adskiller sig i, hvad de analyserer, og hvornår:
Ændre konsekvensanalyse bestemmer omfanget af en foreslået kodeændring, før den foretages. Den identificerer, hvilke moduler, funktioner, databasetabeller og afhængige systemer der skal ændres eller testes igen som følge af den foreslåede ændring.
Testkonsekvensanalyse (TIA) er en specifik anvendelse af ændringskonsekvensanalyse, der identificerer, hvilke eksisterende tests der er relevante for en given kodeændring. I stedet for at køre hele testsuiten vælger TIA den minimale delmængde af tests, der dækker den ændrede kode og dens afhængige komponenter, hvilket reducerer testudførelsestiden, samtidig med at dækningen af det berørte omfang opretholdes.
Analyse af kravenes konsekvens identificerer hvilke krav, designelementer og downstream-leverancer der påvirkes, når et krav ændres. I regulerede brancher sikrer dette, at alle downstream-artefakter, der er afhængige af et ændret krav, opdateres og verificeres igen.
Alle tre deler et fælles fundament: en afhængighedsmodel, der repræsenterer, hvordan komponenter relaterer sig til hinanden, og en mekanisme til at gennemløbe denne model fra et startpunkt (den ændrede komponent) for at opregne alt, hvad der kan nås fra den.
De tre typer af konsekvensanalyse
Konsekvensanalyseteknikker klassificeres efter, hvordan de indsamler afhængighedsoplysninger:
| Type | Metode | Hvad den finder | Hvornår skal den bruges ? |
|---|---|---|---|
| Statisk påvirkningsanalyse | Analyserer kildekoden uden at udføre den | Alle syntaktiske referencer: funktionskald, import, feltadgang, skemareferencer | Før implementering, under ændringsplanlægning; fungerer på enhver kodebase |
| Dynamisk effektanalyse | Instrumenter, der kører kode for at observere faktiske udførelsesstier | Kun komponenter, der rent faktisk blev brugt under en testkørsel | Runtime-specifikke afhængigheder; identificerer stier, som statisk analyse kan overse |
| Kravbaseret (semantisk) | Sporer sporbarhedsforbindelser mellem krav, design og kode | Opstrøms- og nedstrømsartefakter påvirket af en kravændring | Regulerede brancher; systemteknik; sikkerhedskritisk software |
Statisk konsekvensanalyse er den mest anvendte, fordi den udelukkende fungerer på kildekode uden at kræve et kørende system eller testinfrastruktur. Det er den teknik, der bruges af værktøjerne i denne vejledning og af SMART TS XL til virksomhedskodebaseanalyse. Dynamisk analyse supplerer statisk analyse ved at registrere runtime-adfærd, såsom dynamisk konstruerede forespørgsler eller sent afgrænsede funktionskald, som statisk analyse ikke kan løse udelukkende fra kildekoden. I praksis kombinerer de fleste programmer til produktionspåvirkningsanalyse begge dele: statisk analyse giver det grundlæggende afhængighedskort, og dynamisk profilering validerer det i forhold til observeret runtime-adfærd.
Statisk vs. dynamisk effektanalyse: Nøgleforskelle
Statisk konsekvensanalyse er konservativ: den kan overvurdere det berørte omfang ved at inkludere afhængigheder, der findes i kode, men aldrig udføres i praksis. Dynamisk konsekvensanalyse er præcis i forhold til, hvad den observerer, men ufuldstændig. Den indfanger kun, hvad der rent faktisk kørte under den instrumenterede session, og mangler stier, der udføres under forskellige input eller konfigurationer. For produktionssystemer, hvor fuldstændighed er vigtigere end præcision, er statisk analyse den sikreste standard.
Konsekvensanalyseprocessen: Trin for trin
En struktureret effektanalyseproces følger en ensartet rækkefølge uanset hvilket værktøj der anvendes:
Trin 1: Definer ændringen. Identificer præcis, hvad der ændres: den specifikke funktion, det felt, den klasse, det modul, den kopibog eller den databasekolonne. Præcision på dette trin bestemmer nøjagtigheden af alt, der følger. Vage ændringsdefinitioner ("vi ændrer betalingsmodulet") giver vage konsekvenser.
Trin 2: Byg eller forespørg afhængighedsmodellen. Afhængighedsmodellen repræsenterer relationerne mellem alle komponenter i systemet. For automatiserede værktøjer er denne model bygget ved at parse kildekode. Til manuel analyse på små systemer kan den vedligeholdes som dokumentation. Modellen skal være aktuel: forældet afhængighedsdokumentation producerer unøjagtige konsekvensanalyser.
Trin 3: Gennemgå afhængighedsgrafen fra ændringspunktet. Startende fra den ændrede komponent, følg alle indgående afhængighedskanter (komponenter, der afhænger af den ændrede komponent) og udgående kanter (komponenter, som den ændrede komponent afhænger af, som kan opføre sig anderledes efter ændringen). Fortsæt transitivt, indtil alle tilgængelige afhængige komponenter er opregnet.
Trin 4: Klassificer berørte komponenter efter risiko. Ikke alle berørte komponenter har lige stor risiko. En komponent, der direkte kalder en ændret funktion, har højere risiko end en, der er fjernet med fem afhængighedsniveauer. Klassificer fund efter nærhed, kritikalitet og testdækning for at fokusere afhjælpningsindsatsen.
Trin 5: Definer testomfanget. Impact-sættet, den komplette liste over berørte komponenter, definerer det minimale testomfang. Enhver komponent i impact-sættet, der mangler automatiseret testdækning, repræsenterer en risiko, der skal håndteres enten ved at tilføje tests eller ved manuel validering.
Trin 6: Dokumentér og gennemgå. Præsenter konsekvensanalysen for ændringsrådgivningsudvalget (CAB) eller relevante interessenter som grundlag for godkendelse af ændringen. Det opregnede konsekvensomfang med risikoklassificering erstatter udviklerens estimater med strukturelle beviser.
Testpåvirkningsanalyse: Sådan fungerer det i CI/CD
Testimpactanalyse (TIA) anvender impactanalyse specifikt på testproblemet: hvilke tests skal køres givet en kodeændring? Uden TIA kører CI-pipelines hele testsuiten på hver commit. I en kodebase med 50,000 tests og en testsuite, der tager 45 minutter at køre, betyder det, at hver pull-anmodning blokerer i 45 minutter, hvilket er grunden til, at udviklere routerer uden om den, pusher flere commits uden at vente på resultater og mister den feedback-loop, som testning skal give.
TIA bryder dette ved at spore kortlægningen mellem kode og test: hvilke kodelinjer er dækket af hvilke test. Når en commit ændrer specifikke linjer, vælger TIA kun de test, der dækker disse linjer og deres afhængige. En ændring, der berører tre filer ud af 50,000, kræver muligvis 200 test i stedet for 50,000. Pipelinen kører på sekunder i stedet for minutter.
Kortlægningen er bygget ved at instrumentere testudførelsen til at registrere dækningsdata og derefter lagre disse dækningsdata indekseret af den kode, de dækker. Ved hver ny commit, TIA:
- Identificerer hvilke filer og funktioner der er ændret (fra git diff'en)
- Slår op, hvilke tests der dækker disse filer og funktioner
- Tilføjer tests, der dækker enhver komponent i den statiske afhængighedsgraf for den ændrede kode
- Kører den valgte delmængde; bestået alle resterende tests som formodet upåvirket
Værktøjer, der implementerer TIA, inkluderer Microsofts Test Impact Analysis i Visual Studio, Parasofts TIA-motor, Gradles testudvælgelse og adskillige CI-integrerede plugins til Jest, pytest og andre testkørere. Nøjagtigheden af TIA afhænger af nøjagtigheden af afhængighedsmodellen. Et værktøj, der kun sporer direkte kodedækning uden afhængighedsgennemgang, vil overse tests, der dækker komponenter tre niveauer væk fra ændringen.
TIA i praksis: Før og efter
I en typisk backend-tjeneste til virksomheder reducerer aktivering af TIA testudførelsestiden med 60-80 % af den gennemsnitlige pull-anmodning. Ulempen er, at meget store ændringer, dem der berører delte værktøjer, basisklasser eller udbredt konfiguration, stadig kan udløse store testundersæt. TIA giver mest værdi til funktionsudvikling og fejlrettelser, hvor ændringerne er lokaliserede. For tværgående ændringer som opgraderinger af frameworks eller ændringer af delte skemaer er en fuld testkørsel stadig det sikreste valg.
Konsekvensanalyse i krav- og forandringsledelse
Inden for systemteknik og reguleret softwareudvikling strækker konsekvensanalyse sig ud over kode til hele artefaktkæden: krav, designspecifikationer, testcases, risikovurderinger og verifikationsbeviser. Et ændret krav påvirker ikke kun kode, det påvirker alle designelementer, der implementerede kravet, alle testcases, der verificerede det, alle risikovurderinger, der antog det, og alle compliance-dokumentationer, der refererede til det.
Kravbaseret konsekvensanalyse bruger sporbarhedslinks til at opregne dette downstream-omfang. En sporbarhedsmatrix, der forbinder hvert krav med dets implementeringsdesignelementer, testcases og verifikationsbeviser, gør det muligt at identificere det fulde omfang af den reverifikation, der kræves af enhver kravændring. I regulerede industrier, medicinsk udstyr under FDA 21 CFR del 11, luftfartssoftware under DO-178C, bilsoftware under ISO 26262, er dette reverifikationsomfang et lovgivningsmæssigt krav, ikke en valgfri kvalitetspraksis.
Forbindelsen mellem kravkonsekvensanalyse og kodekonsekvensanalyse er sporbarhed: Når et krav kan spores tilbage til en specifik softwarekomponent, og denne komponent identificeres i en konsekvensanalyse på kodeniveau, kan resultaterne af konsekvensanalysen bruges til at fokusere genverifikationsindsatsen på de specifikke testcases, der verificerer den pågældende komponent. Moderne kravstyringsplatforme, herunder Jama Connect og IBM DOORS, understøtter denne sporbarhed og giver indbyggede konsekvensanalysefunktioner på kravniveau.
Konsekvensanalyse for store og ældre kodebaser
Konsekvensanalyse for store kodebaser, især virksomhedssystemer, der er vokset over årtier, er kvalitativt forskellig fra konsekvensanalyse for en tjeneste med 10,000 linjer. Skalaforskellene er ikke kun kvantitative. Store ældre kodebaser har afhængighedsstrukturer, som intet levende teammedlem fuldt ud forstår: tusindvis af programmer med implicit kobling gennem delte datasæt, kopibøger inkluderet af hundredvis af programmer samtidigt, JCL-jobstrømme med kompleks betinget udførelseslogik, der skaber afhængigheder kun til runtime.
Flere karakteristika ved store kodebaser gør manuel konsekvensanalyse upålidelig:
Implicitte afhængigheder. I COBOL-systemer skaber en kopibog, der er inkluderet i 300 programmer, en afhængighed, der er usynlig for enhver udvikler, der ikke ved, hvornår de skal lede efter den. En ændring af et kopibogsmedlem, der ligner et feltomdøb, kan kræve genkompilering og gentestning af alle 300 programmer. Uden automatiseret analyse opdages dette omfang gradvist, og hver ny fejl afslører en anden overset afhængighed.
Afhængigheder på tværs af sprog. Et COBOL-program skriver til en DB2-tabel. En Java-tjeneste læser fra den samme tabel. En Python-pipeline behandler Java-tjenestens output. En ændring af DB2-skemaet påvirker alle tre lag. Intet statisk analyseværktøj på ét sprog kan spore denne tværsprogskæde; det kræver et værktøj, der forstår og forbinder alle tre sprog i en samlet afhængighedsmodel.
Indirekte afhængigheder gennem data. To programmer, der aldrig kalder hinanden, kan stadig være koblet sammen via en delt fil. Program A skriver til datasæt X; Program B læser fra datasæt X. En ændring af layoutet af datasæt X påvirker begge, men afhængigheden er ikke et funktionskald, det er en datakontrakt udtrykt gennem JCL DD-sætninger og COBOL FD-definitioner. Strukturanalyse, der kun sporer funktionskald, overser denne klasse af afhængigheder fuldstændigt.
Død kode og tilgængelighed. Store kodebaser akkumulerer kode, der er defineret, men aldrig kaldet, funktioner, der forbliver fra fjernede funktioner, og procedurer, der er blevet erstattet, men ikke slettet. Konsekvensanalyser, der inkluderer død kode i det berørte omfang, overvurderer ændringsomfanget og retter testindsatsen mod komponenter, der aldrig vil blive nået i produktion.
arvemodernisering En analyseløsning til disse miljøer skal håndtere alle disse tilfælde: den skal analysere de faktiske sprog, der er i brug (inklusive COBOL, JCL, PL/I, RPG, Assembler og DB2), løse implicitte afhængigheder gennem delte datastrukturer, spore tværsproglige kæder og skelne mellem tilgængelig og utilgængelig kode.
Værktøjer til effektanalyse: Hvordan de sammenlignes
Værktøjerne nedenfor dækker de vigtigste kategorier af konsekvensanalyse i softwareudvikling. Hvert kategori vurderes ud fra, hvad det analyserer, hvilke sprog det understøtter, og hvilken klasse af konsekvensanalyseproblem det bedst adresserer.
| Værktøj | Primær tilgang | Sprog | bedst til |
|---|---|---|---|
| SMART TS XL | Statisk + tværsproglig afhængighedskortlægning | COBOL, JCL, Java, Python, .NET, RPG, SQL | Konsekvensanalyse af virksomheder og mainframes på flere sprog |
| Forstå med SciTools | Statisk analyse, opkaldsgrafer, visualisering af afhængigheder | 70+ sprog | Flersproget kodeforståelse og effektsæt |
| Struktur101 | Arkitekturanalyse, afhængighedsgrafer | Java, C#, JVM/.NET | Strukturel påvirkning i Java/C# virksomhedsapplikationer |
| CAST AIP | Applikationsintelligens, teknisk gæld, effekt | Java, .NET, COBOL, SQL | Analyse af forretningsmæssig og teknisk effekt på porteføljeniveau |
| Axivion-suite | Semantiske afhængighedsgrafer for C/C++ | C, C ++ | Sikkerhedskritiske systemer, MISRA-overholdelse, indlejrede |
| Parasoft | Testkonsekvensanalyse, CI/CD-integration | Java, C/C++, .NET | TIA i regulerede industrier, sikkerhedskritisk testning |
| Jama Connect | Krav til sporbarhed, artefaktpåvirkning | Sproguafhængig (kravniveau) | Systemteknik, regulerede industrier, DO-178C/ISO 26262 |
| SonarQube | Kodekvalitet, afhængighedsanalyse inden for et sprog | 30+ sprog | Kodekvalitetsporte; begrænset tværsystemkonsekvensanalyse |
| IntelliJ IDEA / Eclipse | IDE-kaldshierarki, referenceanalyse | Java, Kotlin, Python | Lokal konsekvensanalyse på udviklerniveau i et projekt |
Forstå med SciTools er det mest omfattende dedikerede værktøj til konsekvensanalyse til softwareteams, der arbejder i moderne sprog. Dets Impact Sets-funktion beregner den transitive lukning af alle kodeenheder, der er påvirket af en specifik ændring, hver funktion, klasse og variabel, der kan nås via afhængighedsgrafen fra startpunktet. Det understøtter over 70 sprog og producerer detaljerede kaldgrafer, dataflowdiagrammer og entitetsrelationskort.
Struktur101 er det stærkeste værktøj til konsekvensanalyse på Java- og C#-arkitekturniveau. Det visualiserer pakke- og klasseafhængighedsstrukturen som interaktive kort og identificerer, hvor foreslåede ændringer overtræder arkitektoniske grænser eller skaber nye cyklusser i afhængighedsgrafen.
CAST AIP arbejder på porteføljeniveau og analyserer hele applikationslandskabet, herunder COBOL, Java, .NET, SQL og andre sprog, for at producere forretningsmæssige effektscorer sammen med teknisk effektanalyse. Det bruges almindeligvis i M&A due diligence og porteføljerationaliseringsprogrammer.
Axivion-suite sigter mod sikkerhedskritisk C- og C++-udvikling, hvor konsekvensanalyse skal opfylde lovgivningsmæssige krav (ISO 26262, DO-178C, MISRA) og fremlægge formel dokumentation for analysens fuldstændighed.
Parasoft er den stærkeste TIA-løsning til regulerede brancher med en CI/CD-integreret testudvælgelsesmotor, der sporer dækning ned til sætningsniveau og udvælger testundersæt baseret på præcis afhængighedsgennemgang.
SonarQube Leverer afhængighedsanalyse og detektion af kodelugt inden for projektet, men er ikke designet til tværsystem- eller tværsprogskonsekvensanalyse. Dens værdi i konsekvensanalysestakken er som en kvalitetsport, der identificerer hvilke ændrede komponenter, der introducerer nye kvalitets- eller sikkerhedsproblemer, snarere end som en afhængighedskortlægger.
IDE-baserede værktøjer (IntelliJ-kaldshierarki, Visual Studio-referenceanalyse, Eclipse-kaldsgraf) tilbyder lokal konsekvensanalyse for udviklere i et projekt. De er effektive til at forstå, hvad en ændring påvirker i et modul, men kan ikke spore afhængigheder på tværs af projekter, sprog eller mainframes.
Hvordan SMART TS XL Udfører konsekvensanalyse
SMART TS XL udfører konsekvensanalyse ved at analysere alle kildefiler i miljøet, COBOL-programmer, JCL-jobstrømme, kopibøger, SQL-skemaer, Java-klasser, Python-moduler, RPG-programmer og andre, og opbygger en samlet afhængighedsmodel, der repræsenterer alle strukturelle relationer på tværs af alle sprog. Denne model er fundamentet: konsekvensanalyse er en forespørgsel mod den, der starter fra en hvilken som helst komponent og gennemgår afhængighedsgrafen for at opregne alt, der er berørt.
Når et team foreslår at ændre et medlem af en COBOL-kopibog, SMART TS XL's konsekvensanalyse svar: Hvilke programmer inkluderer denne kopibog? Hvilke af disse programmer kaldes af hvilke JCL-jobtrin? Hvilke DB2-tabeller læser eller skriver disse programmer? Hvilke Java-tjenester forbruger disse tabeller? Hvilke testcases dækker disse programmer? Svaret er ikke et estimat, det er en komplet, opregnet liste afledt af den faktiske kodestruktur med filnavne, programnavne, jobnavne og linjenumre.
kortlægning af applikationsafhængighed Funktionen genererer visuelle diagrammer af afhængighedsgrafen centreret omkring den ændrede komponent ved hjælp af farvekodning til at skelne direkte fra indirekte afhængigheder og fremhæve de forbindelser med højest risiko. Disse diagrammer fungerer som evidensgrundlag for CAB-gennemgang og som køreplan for testplanlægning.
JCL udvidelse Funktionen løser symbolsk parametersubstitution i PROC'er før analyse, hvilket sikrer, at afhængighedsmodellen afspejler den faktiske kørselstid snarere end uløste skabelonreferencer. En PROC, der kalder forskellige programmer afhængigt af symbolske parametre, løses op til alle programmer, den rent faktisk kalder på tværs af alle dens kaldere, hvilket giver fuldstændig dækning, som symbolsk ubevidste værktøjer overser.
For virksomhedsteams, der udfører teknisk due diligence, planlægger modernisering af ældre systemer eller administrerer ændringer i systemer, der spænder over flere sprog og platforme, SMART TS XL's virksomhedssøgning Funktionen gør afhængighedsmodellen forespørgbar: find enhver brug af et specifikt felt, ethvert program der kalder en specifik funktion, ethvert JCL-job der producerer et specifikt datasæt, på få sekunder på tværs af en kodebase af enhver størrelse.
Bedste praksis for konsekvensanalyse
Start konsekvensanalysen før du skriver kode, ikke bagefter. Formålet med en konsekvensanalyse er at informere beslutningen om at foretage en ændring og at afgrænse det resulterende arbejde, ikke at forklare, hvad der gik galt efter implementeringen. En konsekvensanalyse, der udarbejdes efter en ændring allerede er i gang, er en efterfølgende rationalisering, ikke et planlægningsværktøj.
Definer eksplicit grænserne for konsekvensanalysen. Impact-grafer i store systemer kan vokse til at omfatte næsten alt. Definer analysegrænsen, den maksimale afhængighedsdybde, den ekskluderede døde kode og systemer uden for omfanget, før analysen køres. Ubegrænset gennemgang producerer resultater, der er teknisk korrekte, men operationelt ubrugelige.
Skeln mellem skal-genteste og bør-monitorere. Ikke alle komponenter i impact-sættet kræver det samme testrespons. En komponent, der direkte kalder den ændrede funktion i en hot path, skal testes igen. En komponent, der når den ændrede funktion gennem fem niveauer af sjældent anvendt kode, kan overvåges i produktion. Risikoklassificering omdanner en impact-liste til en testplan.
Hold afhængighedsmodellen opdateret. En konsekvensanalyse udført mod en forældet eller ufuldstændig afhængighedsmodel er værre end ingen konsekvensanalyse, fordi den skaber falsk tillid til et forkert omfang. Afhængighedsmodeller skal regenereres ved hver væsentlig ændring af kodebasen eller opdateres trinvist via en CI/CD-integration, der automatisk genanalyserer ændrede filer.
Kombinér effektanalyse med forandringskontrol. Konsekvensanalyse giver sin fulde værdi, når dens output indgår i en formel ændringskontrolproces. En konsekvensrapport, der dokumenterer omfang, risikoklassificering og testkrav, giver ændringsrådgivende udvalg den strukturelle dokumentation, de har brug for til at træffe godkendelsesbeslutninger, der er baseret på det faktiske system snarere end på udviklernes estimater.
For ældre systemer skal der tages højde for implicit datakobling. Enhver afhængighedsanalyse for et ældre system, der kun sporer funktionskald, er ufuldstændig. Programmer, der er koblet sammen via delte filer, datasæt, databaser og meddelelseskøer, er almindelige i mainframe-miljøer og er usynlige for analyse af kun funktionskald. Afhængighedsmodellen skal tage højde for kobling på dataniveau for at producere et komplet effektomfang.
Investeringen i infrastruktur til konsekvensanalyse, hvad enten det er gennem et dedikeret værktøj som f.eks. SMART TS XL, en testkonsekvensanalysemotor som Parasoft, eller en platform til sporbarhed af krav som Jama, inddrives gennem omkostningerne ved ændringer, der ikke ødelagde uventede ting, tests, der ikke behøvede at køre hele pakken, og implementeringer, der ikke producerede hændelser. Denne inddrivelse er ikke hypotetisk. Enhver produktionshændelse forårsaget af en uopdaget afhængighed er en direkte omkostning ved analysen, der ikke fandt sted, før ændringen blev foretaget.