Seniorudvikler: Sådan skriver vi selvforklarende kode

Hos det københavnske it-firma e-conomic arbejder man på et indføre en ny kultur for navngivning af kode. Læs blandt andet, hvordan en tekstforfatter hjælper udviklerne til at gøre kode lettere at læse og forstå.
Rasmus Beck, seniorudvikler i e-conomic. Foto: Mikkel Meister

Hvordan døber man variable, metodenavne, klasser og alt det andet, en kodebase består af, så koden kan læses og forstås af udviklere i mange år fremover?

Sådan lyder et af de store spørgsmål, som udviklerne hos det danske softwarefirma e-conomic har sat sig for at give et godt svar på.

Den københavnske virksomhed, som Version2 besøger tirsdag på sommertouren, fremstiller webbaserede regnskabsprogrammer.

Løsninger af den slags, man i dag vil putte ind i kategorien cloud computing. Men firmaet er stiftet i 2001, og dermed rækker både historien og kodebasen hos e-conomic noget længere tilbage end modeordets fødsel.

»Vi er et gammelt hus i den forstand, at vi har været med i lang tid før, det overhovedet hed cloud computing. Vi har oplevet, at organisk vækst i en kodebase betyder afvigelser, og flere og flere udviklere betyder flere og flere afvigelser. På et tidspunkt når man en kritisk masse af udviklere, som ikke har været med fra starten og derfor ikke kender konteksten for al koden,« fortæller seniorudvikler Rasmus Beck, e-conomic, til Version2.

Hans egen karriere i virksomheden begyndte i oktober 2012, og han kunne rimeligt hurtigt se, at virksomheden blev nødt til at finde et fælles fodslag for navngivning af kildekoden.

»Hvis koden ikke er navngivet meget, meget præcist, så er det nødvendigt at tage fat i de udviklere, der er gamle i gårde, for at få forklaret konteksten. Det ville jeg gerne ud over,« siger Rasmus Beck til Version2.

Et helt friskt eksempel fra udviklingsafdelingen er variabelnavnet location, som for et par uger siden førte misforståelser med sig.

For e-conomics egne udviklere var ordet 'lokation' rigeligt specifikt og dækkende. Men bogføring er et domæne præget af en bestemt terminologi, så sådan var det ikke nødvendigvis for andre:

»I vores interne DSL (domænespecifikke sprog, red.) ved folk godt, hvad man taler om, når man siger lokationer. Men jeg talte så med en anden person udefra, som kunne forstå ordet på en anden måde. Og derfor er det enormt vigtigt, at man ud fra navngivningen af bare den ene variable kan sætte en kontekst, som er beskrivende,« siger Rasmus Beck.

I det konkrete eksempel blev navnet location konkretiseret til inventoryLocation for at angive, at der er tale om et nærlager.

Hos e-conomic benytter man Pascal Casing til klasse- og metodenavne og Camel Casing til variable. Det gælder både for C#- og JavaScript-koden, men det har mere at gøre med stil end med selve forståelse af koden.

public class Customer
{
         ...
         public Customer CreateIfNotExists()
         {
                  if (null == id || !Exists(id))
                                               return Create();
 
                  return FetchCustomer();
         }
}

Eksempel på den nye måde at navngive kode hos e-conomic. Metoden CreateIfNotExists hed tidligere bare Create.

For Rasmus Beck er det nu blevet lidt af en personlig mission at indføre en kultur blandt udviklerne, som gør, at koden forklarer sig selv uden at skulle have en uddybende tekst som krykke.

Det kan være fint at skrive fem linjer med forklarende tekst til hver metode, hvis man vedligeholder et offentligt API.

Men for en intern udviklingsafdeling er det bare symptombehandling, mener Rasmus Beck.

Seniorudvikleren har fået hjælp til opgaven af husets egen tekstforfatter, der er ansat specifikt til at skrive dokumentation til koden.

»Det er en disciplin, som er væsentlig sværere at indføre end som så. Det kræver en enighed på tværs af alle udviklere og udviklingsafdelinger, og det kræver en form for domænespecifikt sprog, som man også kan blive enige om,« siger Rasmus Beck.

»Når først man tør bruge tekstforfatteren, får man rigtig mange positive ting ud af det. Det er ud fra en erkendelse af, at udviklere er gode til at skrive kode og ikke altid til at kommunikere,« forklarer Rasmus Beck til Version2.

En vigtig afledt effekt af den nye fremgangsmåde er i sidste ende en mere robust kode, forklarer Rasmus Beck.

»Hvis man har svært ved at navngive præcist, hvad koden dækker over, så sker der for mange ting samme sted, og så bør man refaktorere, så koden bliver mere simpel og lettere forståelig - og dermed også lettere at navngive,« siger han.

På værktøjssiden bruger e-conomic Facebook's Phabricator til code review og repository browsing. Det hjælper med til at understøtte den nye måde at navngive på, hvor udviklerne er så konkrete og entydige som muligt.

Men det har også krævet en ny kultur med en særlig boyscout rule, forklarer Rasmus Beck.

Den indebærer, at alle udviklere, der er inde at røre ved den gamle del af kodebasen, sørger for at opdatere den en smule i overensstemmelse med de nye regler.

»Der vil selvfølgelig opstå navngivning, som er i konflikt med den gamle måde at gøre det. Men vores boyscout rule siger, at du skal efterlade koden pænere end den, du kom til,« siger Rasmus Beck.

Svært at måle

Rasmus Beck oplever nu, at udviklerne i hans eget team har større fokus på navngivningen af koden.

Forskellen fra før til nu er i sagens natur svær at måle. Men konkret kan det ses ved, at de udviklere, der har været med fra begyndelsen, nu får færre spørgsmål til legacy-koden.

»Men det er svært at måle på, så det er mere en blød metrik,« konstaterer han.

Det lyder jo meget godt, men hvis du nu pludselig får nyt job, er det så blevet en fast nok del af kulturen hos udviklerne til at blive videreført?

»Ja, det synes jeg. Jeg har en forståelse af, at folk synes, det er en god idé. Jeg ved, at der er andre udviklere, som har kæmpet den her kamp, før jeg kom til, så jeg er ikke den eneste,« siger Rasmus Beck.

Denne artikel er en del af Version2's sommertour, hvor vi flytter redaktionen ud til en stribe danske it-firmaer. Se hele tour-kalenderen

Tips og korrekturforslag til denne historie sendes til tip@version2.dk
Kommentarer (73)
Pelle Söderling

Jeg er temmelig enig i at kode bør opdeles og brydes ned til små selvforklarende metoder og at navngivning her er uhyre vigtig - jeg bruger ofte mere tid på navngivning end det at skrive koden.

Iøvrigt er det PascalCasing der anvendes i eksemplet, ikke camelCasing.

Men det eksempel der her opstilles er jeg ikke enig i opfylder målet.

For det første: CreateIfNotExists er ikke et specielt godt navn til en public metode - det er at expose intern logik udadtil.

Derudover er if-sætningen kluntet skrevet og der er ikke brugt { } hvilket jeg mener man ALTID bør gøre, det med at skippe dem er blot sloppy og skaber større sandsynlighed for misforståelser når koden læses.

Må indrømme jeg gruer lidt for hvordan resten af koden ser ud, hvis ovenstående er ment som et godt eksempel.

Mads N. Vestergaard

Enige, brugen af curly braces, er super essentiel, netop når vi snakker selvforklarende kode.

Man kan diskutere hvilke metoder der skal være public, men det afhænger meget af konteksten.

Ret interessant at læse om hvordan E-Conomic har valgt at håndtere det problem, vi (nok alle), støder på fra tid til anden.

Selvfølgelig bør vi alle overholde en hvis måde at lave tingene på så alle let kan sætte sig ind i andres kode.

Det kan dog undre mig at E-Conomic med så mange år på bagen, først tager en beslutning om det nu.

Ole Tolshave

Jeg synes nu ikke CreateIfNotExists er så tosset endda. Det beskriver meget godt hvad man kan forvente at metoden gør, og jeg synes bestemt ikke at det er en implementeringsdetalje, at man enten får returneret en eksisterende entitet eller en nyoprettet.

Hvordan metoden udfører sit arbejde er naturligvis en intern detalje, men metodens synlige effekter på databasen er IMHO ret vedkommende for kalderen.

At det så måske oftere kendes som "GetOrCreate" (se evt. http://stackoverflow.com/questions/1462341/getorcreate-does-that-idiom-h...) er en smagssag.

Pelle Söderling

Det er i petitesseafdelingen, men jeg mener det er implementeringslogik der hører til Customer og ikke noget der kommer udefrakommende ved.

Desuden signalerer CreateIfNotExists stadig ikke hvordan det i praksis håndteres - smides der f.eks. en Exception hvis brugeren allerede eksisterer? Returneres der NULL? Eller returneres den eksisterende bruger?

Ergo, har du brug for at håndtere den case - så er du stadig nød til at læse koden for CreateIfNotExists for at finde ud af hvordan det konkret bliver håndteret. Du har højest opnået at hinte om at der er noget specialhåndtering tilstede for denne specifikke case, men det sker på bekostning af en mere teknisk navngivning. IMO er det ikke det tradeoff værd og hvad så med alle mulige andre special cases i den forbindelse - skal vi også tilføje dem til metode-navngivningen?

Jesper Lund Stocholm Blogger

Der er vel ikke noget nyt i det... måske lige at det bliver gjort ;-)


Præcist min tanke. I stedet for at cherry-picke detaljer i eksemplet, der kunne/burde være anderledes, så lad os da klappe i hænderne over, at der er et firma, der rent faktisk tager dette så seriøst, at de hyrer tekstforfattere til at hjælpe.

Enige, brugen af curly braces, er super essentiel, netop når vi snakker selvforklarende kode.


Seriøst? Er du 11 år gammel?

;-)

Curly braces er gode til at "hegne" kompleksitet ind - men at en short-hand notation for IF skulle være "kompleks" går over mit hoved.

Personligt ææælsker jeg hver gang én af "mine" programmører laver kode med

var x = a == b ? c : d;

Jeg har svært ved at se, at dette skulle være mere "selvforklarende":

string s;  
if (1 == 2)  
{  
  s = "hej";  
}  
else  
{  
  s = "farvel";  
}
Claus Tøndering

Jeg er udmærket klar over fordelene ved at benytte krøllede parenteser - også når det strengt taget ikke er nødvendigt.

Men når det er sagt, må jeg også sige at jeg aldrig benytter dem selv hvis de kan undværes; ligesom jeg foretrækker at skrive { på samme linje som if(...) i stedet på en linje for sig.

Hvorfor? Fordi det giver mig et par ekstra linjer kode på skærmen ad gangen. Jo mere kode jeg kan se uden at skulle scrolle op og ned, jo bedre. Men naturligvis stadig under hensyntagen til at koden i øvrigt skal være letlæselig.

Jakob Krarup

I vores udviklergruppe (CGI, Aarhus afd.) har vi også set lyset i Clean Code filosofien. Det er værd at nævne for uindviede at det er Robert C. Martin ("Uncle Bob" blandt fans ;-)) der i høj grad har dannet skole for bl.a. det nævnte "The Boy Scout Principle" via sin bog "Clean Code" (ISBN 0132350882).
Hvis nogen har lyst til at få en kort opsummering har vi lavet et cheatsheet her med tilhørende eksempler taget fra Uncle Bob's bog, samt egen kode: https://dl.dropboxusercontent.com/u/1775783/permanent/Clean%20Code%201.0...

/Jakob

Pelle Söderling

Jesper: Det er jo fint nok at klappe i hænderne over at et firma ønsker at tage navngivning af kode mere seriøst (omend jeg ik helt ved hvad jeg skal mene om tekstforfatter initiaitvet ... )

Jeg finder det blot pudsigt at det eksempel man så vælger at smide op i en offentlig artikel som skal vise resultatet af denne metode stortset ikke indeholder en linje kode der er specielt pænt skrevet eller formuleret på trods af de relativt få linjer der er tale om.

Hvis man stiller sig selv offentlig til skue på den måde, må man også forventes det kommenteres på.

Mads N. Vestergaard

Personligt ææælsker jeg hver gang én af "mine" programmører laver kode med

Siger du så samtidig er dine programmører ikke arbejder ens ? :)

Jeg er med på hvad du mener, men min erfaring, med de programmører jeg har haft med at gøre er at de færreste kan læse den ud af hovedet.

Javidst det er da et spørgsmål om udenads lære, men erfaring siger mig, at de fleste enten slår op hvordan det er, eller endnu værre, antager hvordan tingene forholder sig, og dermed opstår der en fejl.

Om man vælger den ene eller den anden er helt klart en smagssag, hoved formålet må bare være at alle i organisationen gør det ens.

Ole Tolshave

I hvert fald kan man sige, at formålet med at bruge tid på navngivning i og formattering af koden, er at både interne og eksterne udviklere skal blive så lidt overraskede som muligt over hvad koden gør, når de læser den.

Hvordan man opnår det i praksis kan der være rigtig mange gode bud på, men konsistens kan de fleste nok blive enige om er en fordel.

Det kan i praksis være sværere at opnå end man lige skulle tro, men "spejderprincippet" lyder som en særdeles brugbart værktøj i praksis.

Søren Lund

Uanset hvor smart en IDE man evt. bruger (jeg benytter selv udelukkende Emacs), så er det koden man ser, læser, ændrer og udvider i. Derfor er kodestandarder, herunder god navngivning, essentielt.

Hvis jeg undlader tuborg-parenteser, så skriver jeg hele udtrykket på en linie, dvs. ovenstående ville have været:

if (null == id || !Exists(id)) return Create();

Men, jeg ville aldrig have skrevet indmaden af CreateIfNotExists() som i eksemplet, jeg ville have skrevet:

return (id != null && Exists(id)) ? FetchCustomer() : Create();

Kun et "return" er altid rart, og jeg synes at det er mere logisk (= lettere at læse) at checke om id er forskellig fra null og eksisterer end det omvendte udtryk brugt i den oprindelige kode, og det er rart at undgå negeringer.

Jeg ville have kaldt metoden for FetchOrCreate() det forklarer i højere grad hvad metoden gør, og "IfNot" er endnu en negering, som jeg helst undgår.

Pelle Söderling

Fredrik: Det vile være fint hvis koden var private, men når der er tale om public metoder bør man være meget påpasselig med at indføre teknisk og upræcis navngivning - de public metoder repræsenterer klassens API og bør i høj grad være navngivet ud fra usecase-terminologi.

Jeg kan acceptere FetchOrCreate som Søren Lund foreslår - her er der ingen tvivl om hvad resultatet er, det er langt mere koncist navngivet.

CreateIfNotExists er dårlig navngivning fordi det ikke står klart hvad der returneres ved kald af metoden.

Carsten Hansen

Jeg ville foretrække i et ERP-system, at der ikke blev oprettet tomme records. Derudover, så kan det tænkes, at ID er forkert og systemet skulle være fejlet eller i det mindste have lavet en log.

Personligt tester jeg konsekvent for blanke værdier i starten af en funktion.

Hvis det ikke ender med en dyr låsende skrivning i databasen er det selvfølgelig OK - f.eks. hvis der er behov for en databærer i hukommelsen, men der skal hele tiden checkes for blanke værdier.

Thomas Andersen

Jeg mener at kode skal være farvet grøn på sort baggrund! ;)

Det er lidt spændende med den tekstforfatter. Jeg kunne godt tænke mig at vide lidt mere om hvordan samarbejdet foregår i praksis. Laver han review af API'en efter den er skrevet, fungerer han mere som en generel vejleder/underviser under vejs, eller hvordan bruger i ham?

Hvad angår boy scouting, så er jeg umiddelbart en tilhænger. En bagside ved det er dog at det "roder" i historikken, så outputtet fra git blame ikke længere er anvendeligt. Derudover er der selvfølgelig også risikoen for at introducere fejl ved den slags churn.

Pelle Söderling

Nicolai: Helt simpelt - hvordan kan du læse ud af
"public Customer CreateIfNotExists()" hvorvidt den eksisterende customer returneres eller der returneres NULL således at man kan reagerere eller informere om det?

Det fremgår på ingen måde tydeligt uden at læse koden, at det tilfældigvis viser sig at matche din forventning om hvad metoden ville gøre kan du ikke bruge til meget - det vil være en farlig antagelse at basere sig på.

Nicolai Lundgaard

Nicolai: Helt simpelt - hvordan kan du læse ud af
"public Customer CreateIfNotExists()" hvorvidt den eksisterende customer returneres eller der returneres NULL således at man kan reagerere eller informere om det?


Det kan jeg ikke, men det vil blive meget langt hvis hele semantikken skulle koges ned i et metodenavn.

Du ville have den samme problematik ved f.eks. GetCustomer(id). Returnerer den null eller kaster den en exception hvis objektet ikke eksisterer? Så skulle du kalde den GetCustomerOrNullIfNotExist eller noget i den dur. Det bliver for langt efter min smag.

Robert Larsen

Jeg synes heller ikke, at koden er et godt eksempel på god arkitektur.
En 'Customer' klasse ville jeg gætte på repræsenterede et kunde objekt, og det burde være et simpelt objekt med id, navn og så videre. Men meget tyder på, at dette objekt har snabelen direkte ned i databasen, så det er åbenbart også et data access objekt eller anden database lags klasse. Ikke super logisk efter min mening.

Pelle Lauritsen

Personligt ææælsker jeg hver gang én af "mine" programmører laver kode med

var x = a == b ? c : d;

string s;
if (1 == 2)
{
s = "hej";
}
else
{
s = "farvel";
}

For real !?!

For det første bliver det i langt de fleste tilfælde (hvis ikke altid) oversat til det samme maskinkode, så det gælder om at skrive kode (andre) folk kan læse om 10 år, ikke noget der ser smart ud!

For det andet, var det brugen af curley braces, modsat ikke at bruge dem, i one liners der blev kommentaret på.

Og det er altid forkert ikke at benytte sig af dem hvis man laver linieskift, for det eneste der redder dig, er opsætningen af din IDE.

Jeg har set instuktorer på datalogi klovne rundt foran 30 studerende, fordi de ikke kunne finde ud af hvad deres kode gjorde. Og det var netop fordi de havde tilføjet en linie, efter den første linie, efter en if, og troede at den nye også var inde i if-delen ...

Nicolai Nyberg

Jeg er med på hvad du mener, men min erfaring, med de programmører jeg har haft med at gøre er at de færreste kan læse den ud af hovedet.


Jeg er af den holdning, at der godt kan stilles forventninger og krav til en programmør. Såsom at kende operatorerne -- eller tage den fornødne tid til at sætte sig ind i dem når man ikke lige forstår dem. Og har man misforstået dem, så fanges det jo i automatiserede tests ;)

Henrik Høyer

Som jeg læser koden, så vil CreateIfNotExists enten lave en select eller en insert i databasen. Hvad f.. skal man bruge det til.

I min optik skulle denne metode aldrig have været public. Det tyder på at API'et er designet til at man skal lave et customer objekt, udfylde nogle properties, kalde CreateIfNotExists, udfylde nogle yderligere properties, og senere kalde en UpdateInDatabase metode.

Artiklen handler om navngivning, men eksemplet viser et helt andet problem.

Anders Borum

I vores team (MailTalk), er alle (nærmest religiøst) enige om at arbejde efter SOLID principperne (bl.a. etableret af Robert C. Martin, forfatter til en række bøger - herunder den anerkendte "Clean Code" som nævnt tidligere). Det er en stor fordel, at alle tænker og arbejder efter de samme principper - og vi bruger, ligesom e-conomic teamet, også en del resourser på løbende at forstå og revidere forretningssproget (domain language), så processerne er genkendelige i koden.

Målet må være at skrive kode, der er så selvforklarende at en tilfældig (kompetent) programmør kan deltage på teamet uden de store forklarende seancer, og hurtigt være produktiv. Det gælder iøvrigt også for én selv, når man vender tilbage for at revidere logik skrevet af én selv, måneder tidligere :)

I artiklens eksempel tages der udgangspunkt i et Customer objekt; jeg er overrasket over, at ingen rigtigt har kommenteret på, at en CreateIfNotExist operator ikke hører til på et Customer objekt (det er vel ikke objektets ansvar at vide, hvordan det opretter sig selv). Igen, det er et dårligt eksempel og formentligt ikke repræsentivt for, hvordan e-conomics modelerer deres domæne.

David Konrad

@Jesper Lund Stockholm,

Du er vanen tro ude i det argeligste sludder
<code>
"var x = a == b ? c : d;
Jeg har svært ved at se, at dette skulle være mere "selvforklarende":

string s;
if (1 == 2)
{
s = "hej";
}
else
{
s = "farvel";
}
</code>
Det er jo meget nemmere at vedligeholde kode hvor det enkelte udtryk står på én linie frem for mange udtryk på én line. Den compilerede kode er den samme, så hvorfor ikke garnere kildekoden med closures alt det man kan, hvis det hjælper fremtidige programmører?

Fredrik Skeel Løkke

Pelle, jeg er enig i at FetchOrCreate er et bedre navn. Min pointe var at præcis navngivning selvfølgelig også giver mening for en metode, der ikke er skruet fornuftigt sammen. Eksempelvis vil præcis navngivning resultere i et langt kluntet navn for en metode der gør alt for meget! Derved bliver det meget tydeligt at metoden er problematisk.

Troels Henriksen

I min optik er det frygteligt dårlig stil at oprette en variabel uden at initialisere den med det samme. Det gør det langt sværere at etablere invarianter for koden hvis korrekthed kan verificeres blot ved at kigge på erklæringerne. Dér er Stockholms brug af den ternære operator bedre (men selvfølgelig er det reelle program at der bruges defekte sprog, hvor man ikke ville kan skrive 'string s = if ... then ... else ...' uden at det nemt bliver uoverskueligt. Adskillelsen mellem sætninger og udtryk er noget fanden har skabt.

David Konrad

Tværtimod. Herude har vi "namespacing" på klasseniveau. Og så bliver det langt. ClassTypeCollectionDetails->setUpDetails() vs ToolSetupForeignViewer -> blabla() osv. Har man en klasse er man ikke i tvivl om hvad den skal, og så kan de lokale metoder være hent(), get(), skriv() osv altså ustandardiserede

Anders Borum

Angående navngivning på klasser og funktioner, så bør man gøre sig selv (og andre programmører) en tjeneste ved at lægge sig op af standarder i det framework/den platform man arbejder på - og altså ikke opfinde den dybe tallerken, når alternativet allerede foreligger.

Når jeg læser "FetchOrCreate()", vil jeg umiddelbart overveje om "Fetch" optræder regelmæssigt (og bruges konsistent) i kodebasen og således berettiger sin eksistens, eller om alternativet "Get" er mere naturligt.

http://stackoverflow.com/questions/2141818/method-names-for-getting-data

Og hvad angår brugen af linieskift, skal man lige overveje, om det er den primære værdi (let at læse/ændre) eller sekundære værdi (aktuelle features vs. aktuelle behov), der er er i fokus :)

I vores team (MailTalk) arbejder vi med StyleCop (http://stylecop.codeplex.com) og et sæt "shared rules" (som alle på teamet var enige om at vedtage), der sikrer en utrolig høj grad af ensartethed - man kan føle i starten, at man giver noget af sin "personlige stil" fra sig, men i sidste ende er det vigtigere (og rarere), at alle er enige :)

Jesper Lund Stocholm Blogger

Hey David - long time no c :-)

Du er vanen tro ude i det argeligste sludder


Det er helt fint, at du er uenig med mig - men at kalde det for "sludder" er da vist noget ... øehm ... sludder.

Selvfølgelig er der en øvre grænse (et eller andet sted) for hvor meget man kan smide ind på én linie, men at curly braces i sig selv skulle give værdi ifht læsbarhed er jeg bestemt ikke enig i.

I mine øjne skal kode være klar, éntydig og koncis. Min erfaring siger mig, at dette opnås bedst ved at skære ned på mængden af skrevet kode for at udføre "opgave x" og ikke ved at strø curly braces med løs hånd rundt i kildefilerne.

Jesper Lund Stocholm Blogger
Jesper Lund Stocholm Blogger

Siger du så samtidig er dine programmører ikke arbejder ens ? :)


Det må man sige - typisk er teams jo sammensat af programmører, der hver især har noget unikt (i teamet) at bidrage med. Dermed supplerer vi bedst hinanden.

Om man vælger den ene eller den anden er helt klart en smagssag, hoved formålet må bare være at alle i organisationen gør det ens.


Det kan man sige - man skal bare være opmærksom på ikke at ende i "laveste-fællesnævner"-helvedet, hvor den dårligste programmør i teamet sætter standarden. Jeg sørger altid for hele tiden at forsøge at løfte overliggeren ifht kvaliteten af vores kode - også selvom det betyder lidt ekstra tid på at forklare ikke-erfarne programmører om dette eller hint ... eller sætte en senior-programmør på plads, der har skrevet et LINQ-udtryk, der er på den forkerte side af 20 linier (20 linier behørigt skrevet med anvendelse af "composable-engenskaben" i LINQ, naturligvis)

:o)

Allan S. Hansen

Når man ser alt denne debat og holdning til noget så simpelt som lidt kode stumper, så giver det netop mere vægt til argumentet at man lige så godt kan smide en eller to liniers komentar ind hvor det giver mening frem for at forsøge at få ens kode til at være 'selvforklarende'.

Det er lige så meget en vanesag og tidskrævende at smide en linie eller to hist og pist, som det er at overveje hvad man lige kalder metoden eller variablerne i den 'selvforklarende' kode; så man kan lige så godt gøre begge dele.

Fordi selvom man har en virksomheds-politik, så vil forskellige programmøre alligevel have forskellige holdninger og meninger og selvom en går ind og 'overruler' og bestemmer hvad det så skal hedde, så er ikke ensbetydende med det stadig betyder det samme for alle andre i virksomheden.
Og så kan en enkelt kommentar betyde meget forskel - selvom koden er selvforklarende for beslutningstageren.

Anders Borum

Der er forhåbentligt ingen professionelle programmører, der har svært ved at forstå (endsige læse) kode, der gør brug af ternære operatorer ("?:").

Ofte er argumentet for if/else-alternativet, at det gemmer på to eller flere operationer - som man så pakker ind i curlies ({}). Men ofte skjuler dette bare et andet problem, nemlig at funktionen har for meget ansvar og kan refaktoreres til to eller flere funktioner, med det resultat at den oprindelige if/else-sætning kan simplificeres via en ternær operator.

Hvis man sætter 5 - 6 linier af til en funktion, er der ikke meget plads tilbage til reelt indhold, hvis man sjusker med if/else + curlies ({}). I starten virker det begrænsende og direkte urealistisk, men det gør reelt en stor forskel for læsbarheden (der jo er den primære værdi).

Anders Borum

Når man ser alt denne debat og holdning til noget så simpelt som lidt kode stumper, så giver det netop mere vægt til argumentet at man lige så godt kan smide en eller to liniers komentar ind hvor det giver mening frem for at forsøge at få ens kode til at være 'selvforklarende'.

Jeg er lodret uenig i den udtalelse og det er blevet bevist på utallige projekter (af utallige professionelle teams), at kommentarer (og anden dokumentation af kildekode) er en håbløs måde at formidle viden på.

Hvad er ideen (og værdien) i først at bruge tid på at skrive en implementering (med tilhørende tests), og så efterfølgende bruge yderligere tid på at forklare, hvad du har gjort - når forklaringen allerede foreligger?

Her forudsættes naturligvis et vist håndværk, hvor programmøren er i stand til at skrive læsbar kode. Kommentarer er "code smells", der indikerer, at programmøren ikke er bevidst om, hvad han/hun laver - hvorfor ellers forklare det for sig selv?

Hvem har du tænkt dig, skal læse din dokumentation? de andre programmører på teamet? de programmører, der med nærmest en professionel træning i at ignorere grønne linier, arbejder videre på din implementering og med minutters arbejde fjerner betydningen af dine kommentarer?

Intet bliver hurtigere forældet end kildekodedokumentation; hvis du vil dokumentere noget, så skriv et par overordnede A4-sider om systemets arkitektur - og fokusér så efterfølgende på at dokumentere offentligt eksponerede interfaces (og så bliver man pludselig opmærksom på, hvad man eksponerer - hvilket igen sætter fokus på, hvilke komponenter man har i arkitekturen og så videre ..).

Når du læser en bog, er der heller ikke fra forfatterens side indsat kommentarer, der forklarer, hvad der menes, vel? Teksten ér forklaringen :)

Lars Skovlund

I GCC har man lov at skrive (bemærk void-returtypen):

void pass()  
{  
}  
   
int main(int argc, char **argv)  
{  
  argc > 1 ? pass() : pass();  
  return 0;  
}

Det betyder ikke det er en god idé (det er heller ikke lovligt i Java, ved ikke med andre). Jeg bruger selv stort set kun ?: ved initialisering af variable (her giver det noget ekstra udtrykskraft fordi ?: er et udtryk, if en sætning), sjældent/aldrig ved return (fordi man har andre muligheder). Det kan være det er mine return-udtryk der er for lange, men jeg synes netop at if/else er lettere læseligt, især hvis udtrykket indeholder et funktionskald med mange parametre. Ovenstående forsøg blev til netop fordi problemstillingen dukkede op på arbejdet.

Anders Borum

Det kan være det er mine return-udtryk der er for lange, men jeg synes netop at if/else er lettere læseligt, især hvis udtrykket indeholder et funktionskald med mange parametre.

Det ér typisk en "code smell", hvis din funktion tager for mange parametre. Løsningen kan bl.a. være at erstatte en eller flere af parametrene med en anden funktion, men det giver oftest sig selv, når man overvejer hvad funktionens ansvar er :)

Brugen af if/else kan også være en "code smell" der indikerer mangel på polymorfisme.

Troels Henriksen

Her forudsættes naturligvis et vist håndværk, hvor programmøren er i stand til at skrive læsbar kode. Kommentarer er "code smells", der indikerer, at programmøren ikke er bevidst om, hvad han/hun laver - hvorfor ellers forklare det for sig selv?

Hvad med ting som at beskrive invarianter (løkkeinvarianter er et meget udbredt eksempel), beviser for en algoritmes korrekthed, noter omkring køretiden og potentielle forbedringer der ikke var nødvendige da koden først blev skrevet, eller argumenter for hvorfor en tilsyneladende forkert stump kode i virkeligheden er en kritisk optimering?

En anden typisk kommentar er hvorfor f.eks. en fikspunktsiteration altid vil terminere - det er utroligt svært at gøre den slags åbenlyst blot ved navngivning af funktioner og variable, og her er kodekommentarer essentielt. Det kræver naturligvis disciplin at sørge for, at kommentarerne svarer til kodens reelle indhold, men jeg synes man bør have en vis forventning om at ens kollegaer (også de fremtidige) har bare et minimum af professionel stolthed. Jeg mener, ellers kan man argumentere for at invarianter af enhver slags er håbløse, da de jo alligevel vil blive brudt, når fremtidige programmører ikke er opmærksomme på dem.

David Rechnagel Udsen

Jeg er lodret uenig i den udtalelse og det er blevet bevist på utallige projekter (af utallige professionelle teams), at kommentarer (og anden dokumentation af kildekode) er en håbløs måde at formidle viden på.

Gisp ja, hvor ville jeg dog ønske at der ikke var kommentarer i meget af den kode jeg læser, det betyder bare at jeg forstår hensigten med koden, det er røv irriterende.

Det ér typisk en "code smell", hvis din funktion tager for mange parametre. Løsningen kan bl.a. være at erstatte en eller flere af parametrene med en anden funktion, men det giver oftest sig selv, når man overvejer hvad funktionens ansvar er :)

Du har så ret, jeg undgår også fuldstændigt parametre til funktioner, i stedet laver jeg klasser, hvor jeg putter parametrene ind som felter og kalder klassen én metode, som har erstattet den grimme funktion.

At gå fra:

b = add(1, 2);

Til (pseudokode):

add = new Add;  
add.a = 1;  
add.b = 2;  
b = add.Add();

Er så meget mere overskueligt.

Jørgen Larsen

Når man som mig gennemlæser tusindvis af kodelinjer, så er den interne kodekvalitet selvfølgelig helt essentiel fordi jeg ofte ser den for første gang, og skal forstå den meget hurtigt.

De folk, som har skrevet koden, er ofte professionelle og de fleste har formodentlig læst "Clean Code" af Robert Martin og tilsvarende litteratur. Den dårlige kodekvalitet er oftere resultatet af manglende ledelsesmæssig prioritering, end udtryk for en lav professionel standard. Alt mulig respekt til e-conomic her forsøger man da i det mindst.

Det optimale er selvsagt refaktorering og alle de andre buzzword. Men når det ikke er en realistisk option, og man sidder med en rutine på 200 kodelinjer, så værdsætter man sgu kommentar i koden.

Anders Borum

Du har så ret, jeg undgår også fuldstændigt parametre til funktioner, i stedet laver jeg klasser, hvor jeg putter parametrene ind som felter og kalder klassen én metode, som har erstattet den grimme funktion.

Jeg håber vi begge er enige om, at der må være tale om ironisk pseudokode i dit indlæg (tænk nu ud af æsken). Der er ingen her der anfægter brugen af parametre, snarere brugen af for mange parametre. Det er ikke et spørgsmål om enten eller, snarere fordele og ulemper.

Iøvrigt ville et code review af ovenstående naturligvis give anledning til konstruktiv kritik af sammenfald mellem klasse- og funktionsnavn. Men, det er jo alt sammen for at gøre det overskueligt - sammen med kommentarerne, naturligvis.

Anders Borum

Hvad med ting som at beskrive invarianter (løkkeinvarianter er et meget udbredt eksempel), beviser for en algoritmes korrekthed, noter omkring køretiden og potentielle forbedringer der ikke var nødvendige da koden først blev skrevet?

Beviset for en algoritmes korrekthed finder jeg i de tests, der understøtter implementeringen (og som en selvfølge skrives før, under, eller efter implementeringen - om man vælger TDD må være op til den enkelte programmør, men tests bør ultimativt underbygge korrektheden).

Jeg kan ikke lige se, hvorfor noter omkring afviklingstid er relevant (måske hvis man målretter et specifik device - fx. et høreapparat), men det kommer måske an på, i hvilken kontekst man taler om afviklingen af software. Normalt går jeg først efter efter korrekthed (via tests), dernæst hastighed (via målinger) og hvis hastighed er vigtig, er implementeringen skrevet jvfr. dette behov - og ja, så er det naturligvis 100% fair og relevant, at kommentere på dette. Der er jo ingen her der kategorisk afviser kildekodedokumentation; rettere er der fokus på at skrive dem, hvor de reelt giver værdi (som fx. forklaring af detaljer i en optimeret algoritme).

eller argumenter for hvorfor en tilsyneladende forkert stump kode i virkeligheden er en kritisk optimering?

Hvilket naturligvis er en sjældenhed, de fleste kodebaser taget i betragtning. Jeg håber vi er enige; jeg er i hvert fald ikke uenig i dine synspunkter :)

Anders Borum

Den dårlige kodekvalitet er oftere resultatet af manglende ledelsesmæssig prioritering, end udtryk for en lav professionel standard.

En programmør bør/skal ikke bede sin chef om at fortælle ham, hvordan han skal gøre sit arbejde - tests underbygger fx. korrektheden af implementeringen, og den løbende refaktorering er, udover en del af håndværket, med til at gøre koden nem at læse og ændre (den primære værdi), så chefen kan få implementeret sine aktuelle og fremtidige funktioner (den sekundære værdi). Du fortæller jo heller ikke din chef, hvordan han skal gøre sit arbejde, vel? :)

Programmører har ingen ret til at tørre ansvaret for en dårlig kodebase af på ledelsen. Man kan sagtens producere god kvalitet under pres - inden for ret og rimelighed, naturligvis.

Det optimale er selvsagt refaktorering og alle de andre buzzword. Men når det ikke er en realistisk option, og man sidder med en rutine på 200 kodelinjer, så værdsætter man sgu kommentar i koden.

Hvis jeg blev bedt om at ændre på en rutine på 200 linier (som formentligt, alene på sin størrelse, ikke er dækket af tests), er det sandsynligt, at ændringen medfører en eller anden for regressionsfejl. Derfor, for at forstå koden før jeg ændrer den (og gøre den læsbar for fremtidige kollegaer), er det min opgave at simplificere og reducere til flere, men mindre ansvarsfulde funktioner.

Hvis ikke du tager ansvaret på dig, vil dit store tidsforbrug ikke gavne en kollega (eller dig selv). Vi skal, som programmører, simpelthen blive bedre til at rydde op efter os - løbende. Det er den eneste måde vi kan undgå, at kodebasen ikke sander til og problemet kun kan løses ved at bede ledelsen om finansiering til at starte forfra ..

Mads N. Vestergaard

Re: pæn kode?

Siger du så samtidig er dine programmører ikke arbejder ens ? :)

Det må man sige - typisk er teams jo sammensat af programmører, der hver især har noget unikt (i teamet) at bidrage med. Dermed supplerer vi bedst hinanden.

Jeg finder det interessant at en Mand som dig der arbejder netop for standardisering (ISO og dokument formater), ikke har et team der er standardiseret.

Det er vidst noget af en debat der er startet her, og den tager nok ingen inde, for folk har åbenbart en meget fast holdning til det.

Men jeg kan kun sige, hvis jeg skal vælge mellem en udvikler sig der vil indordne sig under den ene eller den anden art, omend det måske strider mod hans egne principper, kontra en der står fast på sin holdning, tror jeg den første vil være mit valg, han vil uden tvivl være langt bedre til at sætte sig ind i virksomhedens ellers kundens behov, fremfor altid at gøre det han mener er rigtigt.

Jørgen Larsen

@Anders Borum - Tak, for det politisk korrekte svar. Hele denne svanesang kunne jeg så ment godt skrive selv. Pointen i min kommentar er sådan set dækket af den konklusion Du selv når frem til:

Det er den eneste måde vi kan undgå, at kodebasen ikke sander til og problemet kun kan løses ved at bede ledelsen om finansiering.....

Virkeligheden er blot for rigtig mange, at den største forhindring IKKE er hvorvidt IT folk forstår den problematik. Det gør langt de fleste og altså også ledelsen hos e-conomic.

Troels Henriksen

Beviset for en algoritmes korrekthed finder jeg i de tests, der understøtter implementeringen (og som en selvfølge skrives før, under, eller efter implementeringen - om man vælger TDD må være op til den enkelte programmør, men tests bør ultimativt underbygge korrektheden).

Det mener du vel forhåbentligt ikke alvorligt? Afprøvning er af stor betydning, men de fungerer ikke til alle formål. Lad mig tage et konkret eksempel fra min dagligdag, hvor jeg arbejder på en oversætter. Her implementerer jeg ofte kodetransformationer, hvis korrekthed er baseret på den invariant at alle variabelnavne i det program der transformeres er unikke. Dette er yderst relevant at nævne i en kommentar, da en programmør der læser min kode ellers vil være usikker på, om implementeringen rent faktisk er korrekt.

Tests kan vise at noget virker, ikke hvorfor det virker.

Et andet eksempel, hvor årsagen til en kommentar skyldes et workaround til hardware- og systemdesignmæssige omstændigheder: I en operativsystemskerne var det kun muligt at give en enkelt 32-bit værdi med til nye kernetråde (dette er meget almindeligt), men jeg havde et scenarie hvor den nye tråd havde behov for at tilgå adskillige maskinord. En typisk løsning ville være at allokere noget data på hoben og give den nye tråd adressen, men denne kerne understøttede ikke dynamisk allokering. Min løsning var at forældertråden allokerede strukturen på sin egen stak og gav den nye tråd adressen på denne stak-allokerede struktur. Forældertråden ville derefter busywaite på et status-felt i strukturen indtil den nye tråd var færdig med at kopiere de nødvendige data ud af strukturen, og satte feltet til en anden værdi. Når man laver en så usædvanlig manøvre er det ret brugbart at give en grund til hvorfor, og argumenter for hvorfor koden er korrekt.

Jeg kan simpelthen ikke se hvordan du ville kommunikere ovenstående med en afprøvning.

Anders Borum

Virkeligheden er blot for rigtig mange, at den største forhindring IKKE er hvorvidt IT folk forstår den problematik. Det gør langt de fleste og altså også ledelsen hos e-conomic

Bare til din information, har jeg på intet tidspunkt kommenteret på e-conomics metoder; det er mit indtryk at de har fokus på de rigtige værdier. Jeg kommenterer på debatten generelt, fordi der lader til at være meget forskellige holdninger til håndværket og hvad man vil give videre til sin kollega.

Og virkeligheden er desværre, at rigtigt mange programmører ikke forstår, at det kan have ret store konsekvenser (bl.a. økonomisk), at fokusere på den sekundære værdi, frem for den primære værdi.

Og jeg mente iøvrigt "at kodebasen sander til", men nåede ikke at rette det i tide i min forrige kommentar ;)

Anders Borum

Det mener du vel forhåbentligt ikke alvorligt? Afprøvning er af stor betydning, men de fungerer ikke til alle formål.

Jeg mener det ret bogstaveligt. Og nej, afprøvning ér af stor betydning men tjener ikke alle formål. De fungerer som et sikkerhedsnet. Hvordan tester du ellers, at din software virker?

Men når du sætter dig ned for at implementere en algoritme, har du allerede en idé om, hvilke udfald skal svare til hvilke parametre og efter min mening er tests en rigtig måde at bevise dette på. Det tjener også det fremtidige formål at validere, om jeg har introduceret regressionsfejl, når implementeringen ændres.

Og iøvrigt tror jeg vi taler forbi hinanden omkring brugen af kommentarer; de har naturligvis deres berettigelse, men sjældent (du bruger selv ordet "usædvanlig" om dit eksempel) inline blandt resten af implementeringen (fordi funktionerne simpelthen er så korte, at de burde være selvforklarende - undtagen i de resterende 1% som vi åbenbart diskuterer p.t.).

Nu hiver du et meget konkret eksempel frem, og det er jo umuligt for mig at sige hverkan fra eller til. Men jeg vil dog sige, at man kommer langt ved at isolere klasser ud via refaktorering og så efterfølgende dokumentere klassen - på klasseniveau (vigtigt), eller eventuelt på funktionsniveau (dem der er eksponeret offentligt).

Jeg kan simpelthen ikke se hvordan du ville kommunikere ovenstående med en afprøvning.

Hvis jeg har isoleret og testet de enkelte komponenter hvér for sig, er der stor sansynlighed for, at de også fungerer sammen. Men i dit eksempel (igen, uden forudgående viden), kan der være tale om at skrive en integration test der afprøver komponenterne i et specifikt miljø på en specifik hardwarearkitektur (som man så må automatisere på en eller anden måde, eller, forestå en manuel test af som sidste udvej).

Hvis én af dine kollegaer retter noget i ovenstående, hvordan sikrer han sig mod, at der ikke er introduceret regressionsfejl?

Debatten er for mit vedkommende i kontekst af kode skrevet i fx. C# (dvs. på .NET platformen), men mit indtryk er, at de fleste objektorienterede sprog ligger under for de samme behov som vi diskuterer - de tillader i hvert fald den struktur, der faciliterer skrivning af testbar kode.

Det er en uendelig debat, men jeg vil gerne fortsætte over en øl :)

Allan S. Hansen

Jeg er lodret uenig i den udtalelse og det er blevet bevist på utallige projekter (af utallige professionelle teams), at kommentarer (og anden dokumentation af kildekode) er en håbløs måde at formidle viden på.

Når der nu er utallige af professionelle teams der har bevist de ting, så kunne det være sjovt at se de beviser; især sammenholdt med effekten af 'selvdokumenterende kode'.
Fordi ud fra din 'bevis' udtaelse, så må de teams jo have tilsvarende beviser vedrørende selvforklarende kodes evne til at formidle viden

Når du læser en bog, er der heller ikke fra forfatterens side indsat kommentarer, der forklarer, hvad der menes, vel? Teksten ér forklaringen :)

Nu har jeg så svært ved at se en bog som et stykke software produkt opbygget af kode, men jeg må indrømme i en stor del af det litteratur jeg læser er der indsat fodnoter med henvisning til forklarende/uddybende/bevis tekst.
Så jo - der er sådan set 'kode kommentar' i bøger.

Anders Borum

Nu har jeg så svært ved at se en bog som et stykke software produkt opbygget af kode, men jeg må indrømme i en stor del af det litteratur jeg læser er der indsat fodnoter med henvisning til forklarende/uddybende/bevis tekst.
Så jo - der er sådan set 'kode kommentar' i bøger.

Det sagde jeg heller ikke, jeg siger bare, at der er et vist forhold mellem tekst og kommentarer og at mange programmører har en idé om at de fleste kodelinier skal forklares med grøn tekst.

Og bemærk iøvrigt smiley :)

Jens Dalsgaard Nielsen

helt enig med Pelle :-)

der er åbenbart sket en del fra 1956 til 1959 ;-)

Jens - født 1959

en kommentar til tråden generelt. Det er altid en god ide at give funktioner/objekter/typer/variable beskrivende navne. Med "name completion" ved editering i moderne tools er det ikke et stort problem med lange navne. Og hold tabulering på 2 eller 4 så er der plads nok på skærmen.

Jesper Høgh

Føler man sig som konfigurateur som programmeur, så fred være med det. Alt for at hæve selvværet hvor det er behov nødig.

Kald jer hvad i vil, programmering kan i ikke.

Giv et ungt menneske den simpleste f.eks. pic 12 og en f.eks. 24fc256 og bed vedkommende om at lave et gennemsigtigt read/write system. Du får: "awah" hvilket system bygger vi på? Hvis kode skal vi genbruge for at kunne dette umulige?

Per Hansen

Kald jer hvad i vil, programmering kan i ikke.


Det er kun COBOL programmerer, som programmerer rigtigt i følge din defintion? ;)
COBOL hvis 5 statements alle kan lære på en halv time, og hvor der skal skrive 4 siders kode for at addere 3 variable ? :)

Fair nok, hvis det er "programmering" så er jeg ikke programmør. (og ja, jeg kan udemærke konfigurere i COBOL, men vil ikke nedlade mig til at sætte det på mit CV).

Jesper Lund Stocholm Blogger

Jeg finder det interessant at en Mand som dig der arbejder netop for standardisering (ISO og dokument formater), ikke har et team der er standardiseret.


Pudsigt - jeg finder det interessant, at din hjerne i det hele taget tillod dig at lave den sammenkobling ... og endda endte med at sige, at "den sammenligning sidder sgu lige i skabet".

Men du misforstår mig. De teams jeg har været på har naturligvis haft kodestandarder - både nedskrevne og mundtligt aftalte. Vi har haft værktøjsunderstøttelse til at "håndhæve" disse standarder. Vi har haft peer-review, kodereview, code-coverage-værktøjer og alt andet, hvad man kan finde på at smide efter det at skrive kode.

Men hvert team er jo forskelligt. Eksemplet med short-hand-IF er et eksempel på en kamp jeg tabte i sidste team - så her blev der reklameret for Tuborg mere end jeg brød mig om. Men andre kampe "vinder" jeg så - i sidste ende er kodestandarder jo et kompromis imellem de enkelte medlemmer af teamet.

OG så lige en enkelt kommentar til "selvforklarende kode": Jeg køber den ikke - helt. "Selvforklarende" kan forklare, hvad noget kode gør ... med behørig navngivning, struktur etc. Men "selvforklarende" har udfordringer med at forklare hvorfor og ifbm review og vedligehold er dette imo lige så vigtigt. Hvorfor forklares i øvrigt efter min mening bedst med kommentarer i kildekoden.

Jesper Høgh
Robert Larsen

som at nægte at kalde personer, som skriver java, programmører.


Og jeg vil gå så langt som til at kalde dig et surt gammelt røvhul med et selvværd så lavt, at du er nødt til at forsøge at nedgøre andres arbejde for at få dig selv lidt højere op.

Løst hevet ud fra Wikipedia:
Programmører skriver programmer, programmer skrives i programmeringssprog, programmeringssprog er kunstige sprog som kan udtrykke algoritmer præcist. Det kan Java, og derfor er Java et programmeringssprog, og skriver man i Java er man derfor programmør.

Om man så er ekspert, som kan løse alle verdens problemer, er et andet spørgsmål...mon ikke også vi kan finde en programmeringsopgave, som du ikke kan løse? Ikke fordi du ikke er programmør, men fordi du ikke kender nok til platformen eller problemet?

Morten Jensen

Men hvert team er jo forskelligt. Eksemplet med short-hand-IF er et eksempel på en kamp jeg tabte i sidste team - så her blev der reklameret for Tuborg mere end jeg brød mig om. Men andre kampe "vinder" jeg så - i sidste ende er kodestandarder jo et kompromis imellem de enkelte medlemmer af teamet.

Jeg bruger altid tuborgklammer hvis bodyen indeholder andet end et semikolon. Eksempler:

while(Status_Get() == Status_BUSY);

if(...) {
x = 5;
}
else {
x = 3;
}

.. og det gør jeg dels for at undgå at man misforstår en if-sætnings scope, men også fordi jeg har set et SVN-merge snige en tom linie ind eller rykke noget kode ned på næste linie osv. Det er selvfølgelig et problem der opstår fordi Subversion er noget slam, men jeg bruger det, så derfor kommer der klammer om alt.

Ang. kommentarer:
Jeg arbejder pt. med embedded, så det meste foregår i C og assembly, og der vil jeg give Troels ret i at kommentarer kan være en kæmpe hjælp.
Low-level kode kan være svært gennemskuelig uden kommentarer, og hvis TRM'en indeholder fem-ti linier der forklarer alt, så paster jeg det sgu ind i en kommentar over koden.

Log ind eller Opret konto for at kommentere
Pressemeddelelser

Welcome to the Cloud Integration Enablement Day (Bring your own laptop)

On this track, we will give you the chance to become a "Cloud First" data integration specialist.
15. nov 2017

Silicom i Søborg har fået stærk vind i sejlene…

Silicom Denmark arbejder med cutting-edge teknologier og er helt fremme hvad angår FPGA teknologien, som har eksisteret i over 20 år.
22. sep 2017

Conference: How AI and Machine Learning can accelerate your business growth

Can Artificial Intelligence (AI) and Machine Learning bring actual value to your business? Will it supercharge growth? How do other businesses leverage AI and Machine Learning?
13. sep 2017