\n \u003Cinput type=\"text\" name=\"superpower\" />\n \u003Cbutton type=\"submit\" is=\"ajax-submit\" data-target=\"hero-container\">\n add new item\n \u003C/button>\n\u003C/form>\n\n\u003Cdiv id=\"hero-container\">\u003C/div>\n","html",[22,1058,1059,1086,1113,1134,1165,1170,1179,1188,1192],{"__ignoreMap":47},[51,1060,1061,1064,1067,1070,1072,1075,1078,1080,1083],{"class":53,"line":54},[51,1062,1063],{"class":65},"\u003C",[51,1065,968],{"class":1066},"s9eBZ",[51,1068,1069],{"class":61}," action",[51,1071,75],{"class":65},[51,1073,1074],{"class":103},"\"/heroes\"",[51,1076,1077],{"class":61}," method",[51,1079,75],{"class":65},[51,1081,1082],{"class":103},"\"post\"",[51,1084,1085],{"class":65},">\n",[51,1087,1088,1091,1094,1097,1099,1102,1105,1107,1110],{"class":53,"line":69},[51,1089,1090],{"class":65}," \u003C",[51,1092,1093],{"class":1066},"input",[51,1095,1096],{"class":61}," type",[51,1098,75],{"class":65},[51,1100,1101],{"class":103},"\"text\"",[51,1103,1104],{"class":61}," name",[51,1106,75],{"class":65},[51,1108,1109],{"class":103},"\"hero\"",[51,1111,1112],{"class":65}," />\n",[51,1114,1115,1117,1119,1121,1123,1125,1127,1129,1132],{"class":53,"line":82},[51,1116,1090],{"class":65},[51,1118,1093],{"class":1066},[51,1120,1096],{"class":61},[51,1122,75],{"class":65},[51,1124,1101],{"class":103},[51,1126,1104],{"class":61},[51,1128,75],{"class":65},[51,1130,1131],{"class":103},"\"superpower\"",[51,1133,1112],{"class":65},[51,1135,1136,1138,1140,1142,1144,1147,1150,1152,1155,1158,1160,1163],{"class":53,"line":205},[51,1137,1090],{"class":65},[51,1139,1046],{"class":1066},[51,1141,1096],{"class":61},[51,1143,75],{"class":65},[51,1145,1146],{"class":103},"\"submit\"",[51,1148,1149],{"class":61}," is",[51,1151,75],{"class":65},[51,1153,1154],{"class":103},"\"ajax-submit\"",[51,1156,1157],{"class":61}," data-target",[51,1159,75],{"class":65},[51,1161,1162],{"class":103},"\"hero-container\"",[51,1164,1085],{"class":65},[51,1166,1167],{"class":53,"line":241},[51,1168,1169],{"class":65}," add new item\n",[51,1171,1172,1175,1177],{"class":53,"line":250},[51,1173,1174],{"class":65}," \u003C/",[51,1176,1046],{"class":1066},[51,1178,1085],{"class":65},[51,1180,1181,1184,1186],{"class":53,"line":256},[51,1182,1183],{"class":65},"\u003C/",[51,1185,968],{"class":1066},[51,1187,1085],{"class":65},[51,1189,1190],{"class":53,"line":262},[51,1191,304],{"emptyLinePlaceholder":303},[51,1193,1194,1196,1198,1201,1203,1205,1208,1210],{"class":53,"line":272},[51,1195,1063],{"class":65},[51,1197,1042],{"class":1066},[51,1199,1200],{"class":61}," id",[51,1202,75],{"class":65},[51,1204,1162],{"class":103},[51,1206,1207],{"class":65},">\u003C/",[51,1209,1042],{"class":1066},[51,1211,1085],{"class":65},[42,1213,1217],{"className":1214,"code":1215,"language":1216,"meta":47,"style":47},"language-javascript shiki shiki-themes github-light github-dark","class AjaxSubmitButton extends HTMLButtonElement {\n connectedCallback() {\n this.addEventListener(\"click\", async (event) => {\n event.preventDefault();\n let html = await ajaxFormSubmit();\n document.getElementById(this.dataset.target).innerHTML = html;\n });\n }\n}\n\ncustomElements.define(\"ajax-submit\", AjaxSubmitButton, {\n extends: \"button\",\n});\n","javascript",[22,1218,1219,1235,1243,1279,1290,1308,1329,1334,1339,1343,1347,1362,1373],{"__ignoreMap":47},[51,1220,1221,1224,1227,1230,1233],{"class":53,"line":54},[51,1222,1223],{"class":57},"class",[51,1225,1226],{"class":61}," AjaxSubmitButton",[51,1228,1229],{"class":57}," extends",[51,1231,1232],{"class":61}," HTMLButtonElement",[51,1234,66],{"class":65},[51,1236,1237,1240],{"class":53,"line":69},[51,1238,1239],{"class":61}," connectedCallback",[51,1241,1242],{"class":65},"() {\n",[51,1244,1245,1248,1251,1254,1257,1260,1262,1265,1268,1271,1274,1277],{"class":53,"line":82},[51,1246,1247],{"class":96}," this",[51,1249,1250],{"class":65},".",[51,1252,1253],{"class":61},"addEventListener",[51,1255,1256],{"class":65},"(",[51,1258,1259],{"class":103},"\"click\"",[51,1261,146],{"class":65},[51,1263,1264],{"class":57},"async",[51,1266,1267],{"class":65}," (",[51,1269,1270],{"class":78},"event",[51,1272,1273],{"class":65},") ",[51,1275,1276],{"class":57},"=>",[51,1278,66],{"class":65},[51,1280,1281,1284,1287],{"class":53,"line":205},[51,1282,1283],{"class":65}," event.",[51,1285,1286],{"class":61},"preventDefault",[51,1288,1289],{"class":65},"();\n",[51,1291,1292,1295,1298,1300,1303,1306],{"class":53,"line":241},[51,1293,1294],{"class":57}," let",[51,1296,1297],{"class":65}," html ",[51,1299,75],{"class":57},[51,1301,1302],{"class":57}," await",[51,1304,1305],{"class":61}," ajaxFormSubmit",[51,1307,1289],{"class":65},[51,1309,1310,1313,1316,1318,1321,1324,1326],{"class":53,"line":250},[51,1311,1312],{"class":65}," document.",[51,1314,1315],{"class":61},"getElementById",[51,1317,1256],{"class":65},[51,1319,1320],{"class":96},"this",[51,1322,1323],{"class":65},".dataset.target).innerHTML ",[51,1325,75],{"class":57},[51,1327,1328],{"class":65}," html;\n",[51,1330,1331],{"class":53,"line":256},[51,1332,1333],{"class":65}," });\n",[51,1335,1336],{"class":53,"line":262},[51,1337,1338],{"class":65}," }\n",[51,1340,1341],{"class":53,"line":272},[51,1342,253],{"class":65},[51,1344,1345],{"class":53,"line":282},[51,1346,304],{"emptyLinePlaceholder":303},[51,1348,1349,1352,1355,1357,1359],{"class":53,"line":294},[51,1350,1351],{"class":65},"customElements.",[51,1353,1354],{"class":61},"define",[51,1356,1256],{"class":65},[51,1358,1154],{"class":103},[51,1360,1361],{"class":65},", AjaxSubmitButton, {\n",[51,1363,1364,1367,1370],{"class":53,"line":300},[51,1365,1366],{"class":65}," extends: ",[51,1368,1369],{"class":103},"\"button\"",[51,1371,1372],{"class":65},",\n",[51,1374,1375],{"class":53,"line":307},[51,1376,1377],{"class":65},"});\n",[18,1379,1380,1381,1384],{},"Der Vorteil des Custom-Elements gegenüber jQuery (oder sonstiger eigener Implementierung) ist, dass der Browser sich\ndarum kümmert, unser JavaScript mit dem HTML zu verbinden und eine Instanz des ",[22,1382,1383],{},"AjaxSubmitButtons"," zu erstellen.",[18,1386,1387,1388,1391,1392,1397],{},"Kommen zur Laufzeit weitere ",[22,1389,1390],{},"submit"," Buttons mit diesem Attribut hinzu, werden sie vom Browser automatisch mit unserem\ngewünschten Verhalten erweitert. Ist das JavaScript aus irgendwelchen Gründen nicht verfügbar funktioniert weiterhin das\ngute alte HTML Formular mit komplettem Seitenreload. Wir verbessern unsere Anwendung mit jedem weiteren\nTechnologie-Layer, der zur Verfügung\nsteht, ",[540,1393,1396],{"href":1394,"rel":1395},"https://developer.mozilla.org/de/docs/Glossary/Progressive_Enhancement",[544],"Progressive Enhancement"," genannt. HTML\nbeschreibt den Inhalt, CSS macht es bunt, und zu guter Letzt verbessern wir die Benutzererfahrung mit JavaScript.",[18,1399,1400],{},"Das Backend wird mit diesem Ansatz auch nicht komplizierter oder gar komplexer. Im Controller müssen wir erkennen, dass\nes sich um einen Ajax Request handelt. In dem Fall wird ein HTML Fragment gerendert, andernfalls wird die komplette\nSeite gerendert:",[42,1402,1406],{"className":1403,"code":1404,"language":1405,"meta":47,"style":47},"language-java shiki shiki-themes github-light github-dark","@PostMapping(value = \"/heroes\")\npublic String addSuperhero(\n @RequestParam String hero,\n @RequestParam String superpower,\n @RequestHeader(name = \"X-Requested-With\", defaultValue = \"\") String requestedWith,\n Model model\n ) {\n\n model.addAttribute(\"hero\", hero);\n model.addAttribute(\"superpower\", superpower);\n\n if (\"ajax\".equals(requestedWith)) {\n return \"fragments/hero-fragment :: hero-fragment\";\n }\n\n return \"full-page-including-the-hero-fragment\";\n}\n\n","java",[22,1407,1408,1413,1418,1423,1428,1433,1438,1443,1447,1452,1457,1461,1466,1471,1476,1480,1485],{"__ignoreMap":47},[51,1409,1410],{"class":53,"line":54},[51,1411,1412],{},"@PostMapping(value = \"/heroes\")\n",[51,1414,1415],{"class":53,"line":69},[51,1416,1417],{},"public String addSuperhero(\n",[51,1419,1420],{"class":53,"line":82},[51,1421,1422],{}," @RequestParam String hero,\n",[51,1424,1425],{"class":53,"line":205},[51,1426,1427],{}," @RequestParam String superpower,\n",[51,1429,1430],{"class":53,"line":241},[51,1431,1432],{}," @RequestHeader(name = \"X-Requested-With\", defaultValue = \"\") String requestedWith,\n",[51,1434,1435],{"class":53,"line":250},[51,1436,1437],{}," Model model\n",[51,1439,1440],{"class":53,"line":256},[51,1441,1442],{}," ) {\n",[51,1444,1445],{"class":53,"line":262},[51,1446,304],{"emptyLinePlaceholder":303},[51,1448,1449],{"class":53,"line":272},[51,1450,1451],{}," model.addAttribute(\"hero\", hero);\n",[51,1453,1454],{"class":53,"line":282},[51,1455,1456],{}," model.addAttribute(\"superpower\", superpower);\n",[51,1458,1459],{"class":53,"line":294},[51,1460,304],{"emptyLinePlaceholder":303},[51,1462,1463],{"class":53,"line":300},[51,1464,1465],{}," if (\"ajax\".equals(requestedWith)) {\n",[51,1467,1468],{"class":53,"line":307},[51,1469,1470],{}," return \"fragments/hero-fragment :: hero-fragment\";\n",[51,1472,1473],{"class":53,"line":355},[51,1474,1475],{}," }\n",[51,1477,1478],{"class":53,"line":392},[51,1479,304],{"emptyLinePlaceholder":303},[51,1481,1482],{"class":53,"line":400},[51,1483,1484],{}," return \"full-page-including-the-hero-fragment\";\n",[51,1486,1487],{"class":53,"line":408},[51,1488,253],{},[523,1490,1492],{"id":1491},"auf-dem-weg-zur-single-page-application","Auf dem Weg zur Single Page Application",[18,1494,1495],{},"Mittlerweile haben wir die ajax-submit Komponente in unserer Anwendung an sehr vielen Stellen im Einsatz. Bisher hat\nsie sich als robust und einfach bewährt. Jeder im Team hat verstanden wie man es einsetzt und wie es funktioniert. Wir\nbrauchen keine Runtime a la ReactJS, wir müssen Hooks nicht verstehen, wir benötigen kein komplexes Build Setup. Wir\nmüssen nicht überlegen wie wir vue Komponenten integrieren und wir müssen uns keine Gedanken bzgl Client Side only\nFunktionalität machen.",[18,1497,1498],{},"Kurz gesagt: Wir bauen unser Frontend ohne modernes JavaScript Framework und sind (trotzdem) glücklich.",[18,1500,1501],{},"Zugegeben, die Progressive Enhancement Denkweise ist herausfordernd. Daran zu denken und zu überlegen, wie Anforderungen\nohne JavaScript gelöst werden können, hat unsere Industrie vielleicht verlernt? In der Vergangenheit haben mich\nKollegen, die überwiegend im Backend unterwegs sind gefragt, wie man denn heutzutage ein Frontend baut. Nehme man da\nAngular oder React? Über Anforderungen war man sich da aber noch nicht bewusst… Es war zumindest nicht die Einleitung\nzur Technologie Frage.",[1503,1504,1506],"h3",{"id":1505},"herausforderungen-die-kommen-könnten","Herausforderungen die kommen (könnten)",[903,1508,1509],{},[906,1510,1511],{},[1030,1512,1513],{},"Progressive Enhancement Denkweise",[18,1515,1516],{},"Sobald unsere Anwendung einen gewissen Charakter moderner Single Page Applications erreicht hat, wird das natürlich der\nDefault werden (im Geiste). Hier gilt es weiterhin an die Basis zu denken und nicht sofort an JavaScript only Lösungen.",[903,1518,1519],{},[906,1520,1521],{},[1030,1522,1523],{},"History Handling",[18,1525,1526,1527,1532],{},"Vor und Zurück nach bestimmten Aktionen ist bisher keine Anforderung. Sollte diese Anforderung kommen ist das aber auch\nkein Hexenwerk. Nach JavaScript Aktionen die URL mit\nder ",[540,1528,1531],{"href":1529,"rel":1530},"https://developer.mozilla.org/en-US/docs/Web/API/History",[544],"history API"," ändern ist im Bereich des Möglichen 😉",[903,1534,1535],{},[906,1536,1537],{},[1030,1538,1539],{},"State",[18,1541,1542,1543,1548],{},"Nach bestimmten Aktionen den State an mehreren Stellen im Browser aktualisieren. Hier muss denke ich abewägt werden, ob\nBibiliotheken eine Daseinsberechtigung bekommen, oder\nob ",[540,1544,1547],{"href":1545,"rel":1546},"https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent",[544],"Custom Events"," ausreichen.",[799,1550,1551],{},"html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}",{"title":47,"searchDepth":69,"depth":69,"links":1553},[1554,1555,1556,1557],{"id":897,"depth":69,"text":898},{"id":944,"depth":69,"text":945},{"id":1018,"depth":69,"text":1009},{"id":1491,"depth":69,"text":1492,"children":1558},[1559],{"id":1505,"depth":82,"text":1506},[810,811],"2020-06-23T11:20:03","Mit einigen Jahren JavaScript und Reactjs Erfahrung durfte ich Ende letzten Jahres (November 2019) Teil eines neuen\\nTeams und eines neuen Projektes werden. Das Projekt ist ein Traumprojekt jeden Entwicklers. Ein grüne Wiese Projekt mit\\n“freier” Technologiewahl. “Frei” in Form von man darf sich die Zeit für eine Risikoanalyse nehmen und moderne Tools und\\nFrameworks evaluieren.","https://synyx.de/blog/frameworkless-frontend-und-trotzdem-gluecklich/",{},"/blog/frameworkless-frontend-und-trotzdem-gluecklich",{"title":834,"description":844},"frameworkless-frontend-und-trotzdem-gluecklich","blog/frameworkless-frontend-und-trotzdem-gluecklich",[1570,1216,1571],"development","progressive-enhancement","Mit einigen Jahren JavaScript und Reactjs Erfahrung durfte ich Ende letzten Jahres (November 2019) Teil eines neuen Teams und eines neuen Projektes werden. Das Projekt ist ein Traumprojekt jeden Entwicklers. Ein grüne Wiese Projekt mit 'freier' Technologiewahl. 'Frei' in Form von man darf sich die Zeit für eine Risikoanalyse nehmen und moderne Tools und Frameworks evaluieren.","JGt7bZJ_gokf64kVf7XrDHMaEJ2JdAYkb3HTHT7LZEw",{"id":1575,"title":1576,"author":1577,"body":1581,"category":1671,"date":1673,"description":1674,"extension":815,"link":1675,"meta":1676,"navigation":303,"path":1677,"seo":1678,"slug":1585,"stem":1679,"tags":1680,"teaser":1686,"__hash__":1687},"blog/blog/kandddinsky-2019-in-berlin.md","KanDDDinsky 2019 in Berlin",[1578,1579,1580],"burgard","heib","contargo_poetzsch",{"type":11,"value":1582,"toc":1669},[1583,1586,1589,1594,1601,1606,1609,1612,1615,1620,1634,1639,1642,1647,1650,1655,1658,1663,1666],[14,1584,1576],{"id":1585},"kandddinsky-2019-in-berlin",[18,1587,1588],{},"Die KanDDDinsky Konferenz in Berlin fand 2019 in einem Hotel nahe dem Alexanderplatz statt. Ein Tag vor der eigentlichen\nKonferenz gab es zusätzlich einen Workshoptag der DDD Europe. Hier wurden drei tagesfüllende Workshops angeboten, bei\ndenen tiefer und in kleinen Gruppen auf DDD Themen eingegangen wurde. Die Konferenz selbst bestand zum einen Teil aus\neinstündigen Vorträgen, als auch aus zweistündigen “Hands-on” Workshops. Dazwischen gab es in den Kaffee Pausen\nreichlich Snacks sowie Abend- und Mittagessen. Mit ca. 200 Teilnehmern war alles recht übersichtlich und nicht\nüberfüllt. Die Konferenzsäle befanden sich direkt im Hotel, sodass wir nach dem Frühstück gleich starten konnten.\nNachfolgend findet ihr Erfahrungsberichte zu den einzelnen Workshops und Vorträgen, welche uns in guter Erinnerung\ngeblieben sind sowie ein abschließendes Fazit.",[18,1590,1591],{},[1030,1592,1593],{},"Workshop: Thomas Coopman — Event Storming",[18,1595,1596,1597,1600],{},"Der DDD Workshop “EventStorming Introduction” von Thomas Coopman fand am ersten Tag in Kollaboration mit DDD Europe\nstatt. Das Ziel des Workshops EventStorming Introduction war es, den Teilnehmern die Grundlagen von Eventstorming näher\nzu bringen und ihnen zu zeigen, wie sie die Methodik im eigenen Unternehmen einführen können. Dazu haben sich die\nTeilnehmer nach einer kurzen Vorstellung der Problemdomäne am Prozess selbst versucht, unter ständiger Anleitung von\nThomas Coopman. Die Problemdomäne bestand aus einem kurzfristigen Autoverleih (vergleichbar mit den E-Scootern in\nmanchen Städten). Hierzu wurde zunächst klassisch das ",[910,1598,1599],{},"Big Picture"," erarbeitet und das Modell später Schritt für Schritt\nverfeinert und ergänzt. Anschließend wurde der Happy Path im “explicit” und im “reverse” Walkthrough explizit\ndurchgespielt. Interessant war, dass Coopman Brandolinis Prozess angepasst und eine eigene Sprache entwickelt hat:\nBereits während des Big Pictures wurde nicht mit Aggregates und Repositories, sondern mit Commands, die Events\nverursachen, und Rules, die greifen, gearbeitet. Dieser Ansatz hat dabei geholfen User Stories und Acceptance-Tests\nschneller zu finden und Example Mapping effizienter durchführen zu können. Um die Einführung in den Unternehmen zu\nvereinfachen, hat Coopman ständig interveniert und wertvolle Tipps aus seiner umfangreichen Erfahrung mitgegeben, sei es\nfür Teilnehmer (“Domänenexperten sind da um ausgefragt zu werden”) oder für Moderatoren (“mind. 1m Papier/Person”,\n“keine Super-Stickys kaufen”). Im Workshop konnte Thomas viele Kniffe an uns weitergeben — es wäre interessant, an\neinem weiteren Tag zu sehen, wie Coopman den Prozess abgeschlossen hätte und beim Sprung zum Domänenmodell vorgegangen\nwäre.",[18,1602,1603],{},[1030,1604,1605],{},"Workshop: Marco Heimeshoff, Roman Sachse — Why are words, how do they mean?",[18,1607,1608],{},"Einer der “Hands on”-Workshops an den beiden Konferenztagen hieß: “Why are words, how do they mean?” und wurde von\nMarco Heimeshoff und Roman Sachse moderiert. Aus der Beschreibung war nur zu erkennen, dass es sich irgendwie um\nDomain-Driven Design (DDD) drehen musste. Ich erhoffte mir einen praktischen Workshop mit geringem Theorieanteil und\nnach einer kurzen Einführung ging es direkt mit dem praktischen Teil los. Aufgabenstellung war, die Domäne für ein Hotel\nUnternehmen zu entwickeln. Der Raum wurde in mehrere kleine Gruppen aufgeteilt, von denen jede eine Methode des Event\nModeling auf einen Teilbereich der Hotelbranche anwenden durfte (beispielsweise Event Storming oder Domain Story\nTelling). Meine Gruppe wählte den Themenbereich “Verwaltung der Hotelzimmer”. Dabei ging es z.B. darum, wie ein\nKinderbett auf Anfrage des Kunden auf das Hotelzimmer gelangt oder wie die Räume gereinigt werden. Mit Hilfe des Domain\nStory Telling bildeten wir einfache Sätze, welche aus Actors, Activities und Work Objects bestanden — wodurch sich schon\nbald eine Vielzahl von vernetzten Arbeitsabläufen bildete. Hier ließ sich zum einen schon eine Sprache erkennen, zum\nanderen Stellen, welche schwerer zu modellieren waren als andere.",[18,1610,1611],{},"Nach ungefähr 45 Minuten begann der zweite Teil des Workshops. Hier ging es darum die diskutierte Domäne in Programmcode\numzusetzen. Einer der Mentoren bediente die Tastatur und der Raum mit den Teilnehmern konnte bestimmen, in welche\nRichtung entwickelt wurde. Wir begannen einen Teilbereich zu implementieren der zuvor von einer der Gruppen modelliert\nwurde. Die Mentoren sind dabei dynamisch auf Anmerkungen oder Fragen aus dem Teilnehmerkreis eingegangen. Kam eine\nProblemstellung auf, wurde die Lösung direkt erklärt und beispielhaft umgesetzt.",[18,1613,1614],{},"Im ersten Teil des Workshops konnten die Teilnehmer eine Form des Event Modeling mitgestalten und sich in die Domäne\nhineinversetzen. Der zweite Teil hat abstrakte Konzepte des DDD anschaulich erklärt und praktisch umgesetzt. Man konnte\nden Inhalt mitgestalten und sich aktiv beteiligen. Leider dauerte der Workshop lediglich zwei Stunden und die Mentoren\nkonnten nicht auf alle Modelle der einzelnen Teams eingehen. Alles in allem war es ein gelungener Workshop, der sicher\nauch in einem längeren Format gut gelungen wäre.",[18,1616,1617],{},[1030,1618,1619],{},"Vortrag: Roman Sachse — Is Maybe an Option",[18,1621,1622,1623,1626,1627,858,1630,1633],{},"Im Vortrag “Is Maybe an Option” hat sich Roman Sachse an ein grundlegendes Problem gewagt, das in DDD meist\nstillschweigend umgangen wird: ",[910,1624,1625],{},"Nullability im Domain Model."," Sei es die Abwesenheit eines Datenbank-Feldes, der\nRückgabewert einer Funktion, die etwas nicht parse-bares parsen soll oder dass Felder wegen des objektrelationalen\nMappings leer gelassen werden müssen — Sachse hat zu vielen Problemstellungen aus der echten Welt anhand kurzer\nF#-Schnipsel erklärt, mit welchen Strategien sie angegangen werden können. Dabei ist er stark auf die Datentypen\n",[910,1628,1629],{},"Option",[910,1631,1632],{},"Maybe"," eingegangen. Auch wenn sich die Erkenntnisse nicht eins zu eins auf ein Java- oder .NET-Projekt\nübertragen lassen, hat Roman Sachse mit Ansätzen, die sich auch mit Generics und Optionals umsetzen lassen, neue\nDenkanstöße gegeben.",[18,1635,1636],{},[1030,1637,1638],{},"Vortrag: Dennis Doomen — A practical introduction to DDD, CQRS…",[18,1640,1641],{},"Domain-Driven Design, Command-Query-Responsibility-Segregation und Event Sourcing sind Paradigmen, die hervorragend\nzusammenpassen und meist gemeinschaftlich in einem Projekt eingeführt werden. In seinem Vortrag “A practical\nintroduction to DDD, CQRS and Event Sourcing” ist Dennis Doomen zunächst in einem kleinen Rundumschlag auf die Themen\neingegangen und hat sich dann an “echten” Problemstellungen abgearbeitet: “Was passiert, wenn ich das ReadModel anpassen\nmuss?” “Wie trenne ich die Komponenten sauber?” “Was sind Alternativen, durch die ich bestimmte Komponenten ersetzen\nkann?” Der Vortrag hat nicht nur Grundlagen für Einsteiger geliefert, sondern auch Tipps für übliche Probleme. Die\nFolien sind sicherlich eine gute Grundlage für die Verwendung der Paradigmen in großen, änderungsaffinen Projekten",[18,1643,1644],{},[1030,1645,1646],{},"Vortrag: Michael Plöd — Pitching DDD to the management",[18,1648,1649],{},"In den “C-level”-Management-Ebenen wird DDD immer noch oft als esoterischer, teurer Quatsch abgetan — zu versuchen,\nmit Event Storming o.ä. zu punkten, hilft oft nicht. Doch viele der Punkte, die für das Management aktuell wichtig sind,\nsei es beispielsweise Agilität, finden sich in den Ansätzen von Domain-Driven Design wieder. Michael Plöd hat in seinem\nVortrag analytisch dargelegt, wie genau das Paradigma helfen kann, die Ziele der Manager zu erreichen — und dabei Talent\ndarin bewiesen, Management und Entwicklung auf einen gemeinsamen Nenner zu bringen, Argumentationsgrundlagen zu finden\nund mit kleinen Tricks ans Ziel zu kommen. Obwohl viele Ideen aus dem Vortrag analog oder leicht angepasst im nächsten\nMeeting verwendet werden können, ging es um mehr, nämlich um die Denkweise, mit der die unterschiedlichen Sichtweisen\nund Ziele der leitenden und ausführenden Teile zusammengeführt werden können.",[18,1651,1652],{},[1030,1653,1654],{},"Vortrag: Philipp Krenn — Building Distributeted Systems in Distributed Team",[18,1656,1657],{},"Im Vortrag “Building Distributeted Systems in Distributed Team” von Philipp Krenn ging es um die verteilte Arbeit bei\nElastic. Das klassische Büro ist dort eher der Ausnahmefall, die Mitarbeiter sind weltweit verteilt und arbeiten im\nwesentlichen von zu Hause aus. Es wurde aufgezeigt wie dies funktioniert, welche Vorteile es mit sich bringt (wie z.B.\nhohe Flexibilität), aber auch welche Nachteile (z.B. die Remote-Releaseparty fällt eher kleiner aus). Wichtigste\nErkenntnis war für mich, dass es einfacher ist, wenn das gesamte Team verteilt ist, im Gegensatz zu einzelnen,\nverteilten Teammitgliedern (oder an einzelnen Tagen verteilten). Dies deckt sich auch mit der Erfahrung aus eigenen\nProjekten, wo viele klassische Arbeitsplätze im Büro haben und einzelne remote arbeiten.",[18,1659,1660],{},[1030,1661,1662],{},"PARTY SESSION — So You Want to be a Rockstar Developer?",[18,1664,1665],{},"Die Party Session von Dylan Beattie fand am ersten Abend der Konferenz Tage statt. Zum Spaß entwickelte er eine\nProgrammiersprache, die aus Songtexten bestand und veröffentlichte eine Spezifikation auf Github. Er zeigte sehr\nunterhaltsam, wie sich das Projekt in der Open Source Community verselbstständigte. Bald gab es nicht nur die\nSpezifikation, sondern auch die Möglichkeit Programme in „Rockstar“ zu schreiben. Mit humorvollen Beispielen und einer\nGitarreneinlage bildete der Vortrag den perfekten Abschluss des ersten Tages.",[18,1667,1668],{},"Die Kandddinsky ist eine eher kleine Konferenz. Die Vortragssäle sind nicht überfüllt und auch in den Workshops lässt es\nsich gut mitarbeiten. Die Speaker sind Teilnehmer der Konferenz und stehen in den Pausen für Fragen und Diskussionen zur\nVerfügung. Es ist nicht unwahrscheinlich einen der Speaker als Teilnehmer eines Workshops oder Vortrags anzutreffen. Der\nInhalt der Vorträge war oft wenig technisch und auch der Anteil an DDD Themen war weniger als zuvor gedacht. Für DDD\nNeulinge wäre auch eine Einführungsveranstaltung zu Beginn der Konferenz hilfreich gewesen. Für unsere Workshopbesucher\nhat sich der zusätzliche Tag in Berlin gelohnt. Die Sessions mit kleinem Publikum haben konzentriert Wissen vermittelt\nund neue Impulse gesetzt.",{"title":47,"searchDepth":69,"depth":69,"links":1670},[],[810,1672,811],"konferenzen","2019-12-13T13:56:04","Die KanDDDinsky Konferenz in Berlin fand 2019 in einem Hotel nahe dem Alexanderplatz statt. Ein Tag vor der eigentlichen\\nKonferenz gab es zusätzlich einen Workshoptag der DDD Europe. Hier wurden drei tagesfüllende Workshops angeboten, bei\\ndenen tiefer und in kleinen Gruppen auf DDD Themen eingegangen wurde. Die Konferenz selbst bestand zum einen Teil aus\\neinstündigen Vorträgen, als auch aus zweistündigen “Hands-on” Workshops. Dazwischen gab es in den Kaffee Pausen\\nreichlich Snacks sowie Abend- und Mittagessen. Mit ca. 200 Teilnehmern war alles recht übersichtlich und nicht\\nüberfüllt. Die Konferenzsäle befanden sich direkt im Hotel, sodass wir nach dem Frühstück gleich starten konnten.\\nNachfolgend findet ihr Erfahrungsberichte zu den einzelnen Workshops und Vorträgen, welche uns in guter Erinnerung\\ngeblieben sind sowie ein abschließendes Fazit.","https://synyx.de/blog/kandddinsky-2019-in-berlin/",{},"/blog/kandddinsky-2019-in-berlin",{"title":1576,"description":1588},"blog/kandddinsky-2019-in-berlin",[1681,1682,1683,1570,1684,1685],"berlin","conference","ddd","domain-driven-development","konferenz","Erfahrungsbericht der KanDDDinsky Konferenz 2019","LkddH4oMqPym1394NkfmhIu5bWj7Mx9y4njBxfheu0A",{"id":1689,"title":1690,"author":1691,"body":1693,"category":1790,"date":1791,"description":1792,"extension":815,"link":1793,"meta":1794,"navigation":303,"path":1795,"seo":1796,"slug":1697,"stem":1797,"tags":1798,"teaser":1800,"__hash__":1801},"blog/blog/code-with-attitude-part-1-values.md","Code with Attitude – Part 1: Values",[1692],"franke",{"type":11,"value":1694,"toc":1787},[1695,1698,1701,1706,1713,1717,1720,1729,1735,1738,1744,1747,1750,1756,1759,1762,1765,1770,1777,1780],[14,1696,1690],{"id":1697},"code-with-attitude-part-1-values",[18,1699,1700],{},"At synyx we recently came forward with a new tagline, trying to express our general mindset that is distinctive\nconcerning the way we work with clients, the software community and each other. The line is:",[18,1702,1703],{},[1030,1704,1705],{},"Code with Attitude",[18,1707,1708,1709,1712],{},"In the instant I heard this simple sentence for the first time it triggered a multitude of associations, memories and\nemotions that I connect with the word “attitude” in conjunction with software development. One after another all the\nsituations from different projects during my time at synyx came to mind, where a certain expression of attitude helped\non the way to success. This tells me that our marketing dudes did an excellent job expressing the synyx mindset in one\npowerful and simple statement and also that I have ",[910,1710,1711],{},"a lot"," to say about it. This is why I want to share my\ninterpretation of “Code with Attitude” within this blog series.",[523,1714,1716],{"id":1715},"part-1-courage-to-be-true-to-your-values","Part 1 – Courage to be true to your values",[18,1718,1719],{},"As a human being everybody has a bunch of values and moral standards that you deem to be important and that greatly\ninfluence where and how you live and work. Now you are probably thinking of something like “Don’t code guidance software\nfor nuclear weapons” or “If you manager tells you to tweak the emission control software for the new car model – say\nno!”. Although these are obvious examples, this is not quite what I mean as I have never been in an extreme situation\nlike this – nor have 99% of software developers. What I mean is a set of values, that influence your day-to-day work,\nlike the dedication to quality and sustainability, respectful communication or keeping an open mind.",[18,1721,1722,1723,1728],{},"Behind the term “your values” lies more complexity than you might expect. It does not only mean your personal,\nindividual values but also those ",[540,1724,1727],{"href":1725,"rel":1726},"https://synyx.de/wer/werte/",[544],"of your employer"," and – if your employer is a service\nprovider – even the values of your clients. If you are working for your client it is in his best interest when you take\nhis sense of what is right or wrong into account for the actions you take or don’t take. Ideally those three value\nsystems should be mostly overlapping. If they are heavily misaligned this is a situation that you should not be in.",[18,1730,1731],{},[960,1732],{"alt":1733,"src":1734},"Synyx Werte: Nachhaltigkeit, Soziale Verantwortung, Auf dem Weg zur Exzellenz, Leidenschaft, Eigenverantwortung, offen & ehrlich","https://media.synyx.de/uploads/2019/03/synyx_values-768x432.png",[18,1736,1737],{},"The synyx values - sustainability, social responsibility, pursuit of excellence, passion, individuality,\nself-responsibility, honest&candor",[18,1739,1740,1741,1250],{},"Becoming aware of these values is only the first step. The second one is to venerate and defend them, which often needs\none of the core agile values – ",[910,1742,1743],{},"courage",[18,1745,1746],{},"In fact I am convinced that it is an integral part of our job to bring up the courage to act on our own and our client’s\nvalues. But what does this mean in practice?",[18,1748,1749],{},"There are countless examples of situations where that courage is needed. There is the business guy from the “quality is\nkey” company, who wants to cut software tests to deliver faster to the clients. There are multi-team environments in\nthe “collaborative mind” company where every team is keeping to their own instead of collaborating, feeding to the “Us\nand Them” mindset. There is the Project Manager in the “customer centric” company, conducting a\ndesign-up-front-waterfall-big-bang-release project without the feedback of one single customer. There is the\nwhatever-guy from the “security first” company not caring about data encryption. There is the developer in the “open\nsource” company claiming ownership of “his” part of code within the project. There are quality engineers in the “work\nsmarter not harder” company clicking through thousands of pages of manual testing plans. If you witness any of those or\nsimilar situations at your employer or client, it is your obligation as a rational and professional person to address\nand try to improve the issue. More often than not I observe external contractors or employees resigning to the\ncircumstances thinking “Fuck it, I’m doing my job and getting paid for what I am told”. In my opinion this is the\nopposite of “doing your job”. Your job is to do what is best for your employer/client/project and that means to bring up\nthe courage to question established practices, to actively address problems, to call out bullshit if necessary.",[18,1751,1752],{},[960,1753],{"alt":1754,"src":1755},"Scrum Werte","https://media.synyx.de/uploads/2019/03/agile_values.png",[18,1757,1758],{},"For many people this might seem counter-intuitive. The conservative employer thinks he needs people, who do as they are\ntold. The conservative employee keeps to himself being afraid to offend anybody and fearing the repercussions. In fact\nthe opposite is true and both would highly benefit from “showing more attitude”. As an employer you should embrace\nemployees and external contractors, who question things and who challenge everything against your values and their own\nvalues. This is the only way to improve, to produce sustainable products, to come closer to excellence and to create the\nsolutions you really want! As an employee questioning things and standing up for your values makes your dayjob more\nexciting and meaningful instantly! It gives you the chance to do something that you really care about from your own\naccord, thus increasing your motivation and quality of life and consequentially improving the quality of your work\nresults!",[18,1760,1761],{},"For me this is one of the core parts of “Code with attitude” and one of the most basic mindsets that we live and breathe\nhere at synyx. It is the attitude to have the strong intention to understand our client’s value system and needs and to\nhave the courage to actively act in their best interest, while incorporating our own values for our client’s good.",[18,1763,1764],{},"During my work in different projects this mindset has helped me and our clients countless times. I don’t know how often\nwe had to defend our values of sustainability, quality and excellence by emphasizing the importance of things like code\nquality, automated tests, slack time and refactorings to keep a project afloat or even rescue it from certain failure.\nThere were times when clients needed to be reminded of their own core values like customer friendliness, innovativeness,\ndata minimization or accessability. Otherwise their projects would have gone in (for them!) completely undesirable\ndirections.",[18,1766,1767],{},[910,1768,1769],{},"It is part of our job to get to know the client’s values and remind them!",[18,1771,1772,1773,1776],{},"One time I actually witnessed a project manager from a larger corporation with a\nhundreds-of-developers-software-department make the claim that his company is ",[910,1774,1775],{},"not"," a software company and\ntherefore the software doesn’t have to be developed on a professional level. The guy in a room of ten people who spoke\nup was me – the only guy not employed by the company. I love being that guy, it is a great part of what makes my job\nmeaningful.",[18,1778,1779],{},"The bottom line is that to be of maximum value for you and your client you have to be aware of that complex value system\nmentioned above and you have to bring up the courage to act on it. To say “no” at the right times, to say “yes, but” at\nthe right times, to encourage at the right times. Code with attitude!",[18,1781,1782],{},[540,1783,1786],{"href":1784,"rel":1785},"https://twitter.com/indyarni",[544],"@indyarni",{"title":47,"searchDepth":69,"depth":69,"links":1788},[1789],{"id":1715,"depth":69,"text":1716},[810,811],"2018-11-16T12:29:29","At synyx we recently came forward with a new tagline, trying to express our general mindset that is distinctive\\nconcerning the way we work with clients, the software community and each other. The line is:","https://synyx.de/blog/code-with-attitude-part-1-values/",{},"/blog/code-with-attitude-part-1-values",{"title":1690,"description":1700},"blog/code-with-attitude-part-1-values",[1799,1570,829],"agile"," At synyx we recently came forward with a new tagline, trying to express our general mindset that is distinctive concerning the way we work with clients, the software community and each other. The line is: 'Code with Attitude'. In the instant I heard this simple sentence for the first time it triggered a multitude of associations, memories and emotions that I connect with the word 'attitude' in conjunction with software development.","tbrGLbKylIO0pyaul1N1XDF1TGfKGhQk-zGgy0J997U",{"id":1803,"title":1804,"author":1805,"body":1806,"category":2754,"date":2755,"description":2756,"extension":815,"link":2757,"meta":2758,"navigation":303,"path":2759,"seo":2760,"slug":1810,"stem":2761,"tags":2762,"teaser":2763,"__hash__":2764},"blog/blog/wie-meine-entwicklungsumgebung-eingerichtet-ist.md","Wie meine Entwicklungsumgebung eingerichtet ist",[836],{"type":11,"value":1807,"toc":2749},[1808,1811,1814,1817,1840,1843,1846,1861,1867,1870,1952,1971,1974,2082,2085,2100,2103,2134,2137,2219,2329,2336,2343,2595,2598,2601,2743,2746],[14,1809,1804],{"id":1810},"wie-meine-entwicklungsumgebung-eingerichtet-ist",[18,1812,1813],{},"Beim synyx Camp vor zwei Wochen haben wir uns unter anderem über das Setup unserer Entwicklungsumgebungen unterhalten.\nIm Folgenden möchte ich kurz berichten, wie ich meine eingerichtet habe und welche Programme ich in meiner alltäglichen\nArbeit nicht mehr missen möchte.",[18,1815,1816],{},"Kurz angerissen werden:",[903,1818,1819,1826,1833],{},[906,1820,1821],{},[540,1822,1825],{"href":1823,"rel":1824},"https://synyx.de/blog/2018-11-09-entwicklungsumgebung/?page=1#terminal",[544],"Terminal",[906,1827,1828],{},[540,1829,1832],{"href":1830,"rel":1831},"https://synyx.de/blog/2018-11-09-entwicklungsumgebung/?page=1#git",[544],"Git",[906,1834,1835],{},[540,1836,1839],{"href":1837,"rel":1838},"https://synyx.de/blog/2018-11-09-entwicklungsumgebung/?page=1#programme",[544],"Programme",[523,1841,1825],{"id":1842},"terminal",[18,1844,1845],{},"Ich arbeite mit einem MacBook und habe von Anfang an iTerm2 genutzt. Weshalb weiß ich leider nicht mehr. Vielleicht gab\nes damals für das von Haus aus installierte Terminal keine Möglichkeit ein Dark Theme zu verwenden. Vielleicht gab es\ndamals auch nicht die Möglichkeit ein Terminalfenster in der Horizontalen zu splitten. Beides ist mittlerweile möglich.\nAndere von mir genutzte iTerm2 Features kommen mir gerade nicht in den Sinn…",[18,1847,1848,1849,1854,1855,1860],{},"Viel interessanter finde ich jedenfalls die Frage, welche Shell installiert ist und wie sie personalisiert ist. Ich\nverwende ",[540,1850,1853],{"href":1851,"rel":1852},"https://github.com/zsh-users/zsh",[544],"zsh"," und natürlich(?)\nauch ",[540,1856,1859],{"href":1857,"rel":1858},"https://github.com/robbyrussell/oh-my-zsh",[544],"oh-my-zsh",", um meine Shell im Look & Feel anzupassen und mit Plugins\nerweitern zu können.",[18,1862,1863],{},[960,1864],{"alt":1865,"src":1866},"Code Beispiel","https://media.synyx.de/uploads/2019/03/prompt-1-768x578.png",[18,1868,1869],{},"Folgende oh-my-zsh Plugins habe ich installiert:",[903,1871,1872,1893,1921,1937],{},[906,1873,1874,1875],{},"z",[903,1876,1877,1883],{},[906,1878,1879,1880,1882],{},"führt eine Liste von Pfaden, in welche man auf der Shell mit ",[22,1881,823],{}," navigiert und matcht die eingegebenen Zeichen\nauf den meist besuchten Pfad",[906,1884,1885,1886,1889,1890],{},"z. B. führt bei mir der Befehl ",[22,1887,1888],{},"z dot"," zu ",[22,1891,1892],{},"/Users/seber/projects/git/dotfiles",[906,1894,1895,1900],{},[540,1896,1899],{"href":1897,"rel":1898},"https://github.com/lukechilds/zsh-better-npm-completion",[544],"zsh-better-npm-completion",[903,1901,1902,1905],{},[906,1903,1904],{},"drücke Tab, um projektspezifische npm Task Vorschläge zu bekommen",[906,1906,1907,1908,146,1911,146,1914,858,1917,1920],{},"z. B. bekomme ich im Screenshot die Tasks ",[22,1909,1910],{},"lint",[22,1912,1913],{},"lint:watch",[22,1915,1916],{},"start",[22,1918,1919],{},"start:watch"," vorgeschlagen",[906,1922,1923,1928,1933,1936],{},[540,1924,1927],{"href":1925,"rel":1926},"https://github.com/zsh-users/zsh-autosuggestions",[544],"zsh-autosuggestions",[903,1929,1930],{},[906,1931,1932],{},"fange an zu tippen und der Befehl wird ausgegraut vervollständigt",[1934,1935],"br",{},"drücke Pfeiltaste rechts und Enter, um ihn auszuführen",[906,1938,1939,1944,1949,1951],{},[540,1940,1943],{"href":1941,"rel":1942},"https://github.com/zsh-users/zsh-syntax-highlighting",[544],"zsh-syntax-highlighting",[903,1945,1946],{},[906,1947,1948],{},"hebt das auszuführende Programm farblich hervor",[1934,1950],{},"(nützlich um zu sehen, ob ich mich vertippt habe)",[18,1953,1954,1955,1960,1961,1966,1967,1970],{},"Als Prompt habe ich lange Zeit die ",[540,1956,1959],{"href":1957,"rel":1958},"https://github.com/sindresorhus/pure",[544],"pure prompt"," verwendet. Sie ist super schlank\nund zeigt nützliche Infos zum aktuellen git Repository an. Vor wenigen Wochen bin ich jedoch\nzur ",[540,1962,1965],{"href":1963,"rel":1964},"https://github.com/denysdovhan/spaceship-prompt",[544],"spaceship-prompt"," gewechselt. Einerseits um einfach mal wieder\nwas neues auszuprobieren, andererseits weil sie mir von Haus aus die Version der Programmiersprache des aktuellen\nProjektes anzeigt. Im Screenshot oben sieht man nach dem Wechsel in das ",[22,1968,1969],{},"workshop-maze-vr"," Verzeichnis den Hinweis, dass\ndas Projekt ein nodeJS Projekt ist und die bash session aktuell v10.9.0 verwendet. Neben nodeJS werden einige andere\nProgrammiersprachen unterstützt, Java leider nicht.",[18,1972,1973],{},"Die Anordnung der spaceship prompt Dinge und ob sie überhaupt angezeigt werden sollen, kann konfiguriert werden:",[42,1975,1977],{"className":44,"code":1976,"language":46,"meta":47,"style":47},"SPACESHIP_PROMPT_ORDER=(\n time # Time stamps section\n user # Username section\n dir # Current directory section\n git # Git section (git_branch + git_status)\n node # Node.js section\n docker # Docker section\n exec_time # Execution time\n line_sep # Line break\n jobs # Background jobs indicator\n exit_code # Exit code section\n char # Prompt character\n)\n",[22,1978,1979,1989,1998,2006,2014,2022,2030,2038,2046,2054,2062,2070,2078],{"__ignoreMap":47},[51,1980,1981,1984,1986],{"class":53,"line":54},[51,1982,1983],{"class":65},"SPACESHIP_PROMPT_ORDER",[51,1985,75],{"class":57},[51,1987,1988],{"class":65},"(\n",[51,1990,1991,1994],{"class":53,"line":69},[51,1992,1993],{"class":57}," time",[51,1995,1997],{"class":1996},"sJ8bj"," # Time stamps section\n",[51,1999,2000,2003],{"class":53,"line":82},[51,2001,2002],{"class":103}," user",[51,2004,2005],{"class":1996}," # Username section\n",[51,2007,2008,2011],{"class":53,"line":205},[51,2009,2010],{"class":103}," dir",[51,2012,2013],{"class":1996}," # Current directory section\n",[51,2015,2016,2019],{"class":53,"line":241},[51,2017,2018],{"class":103}," git",[51,2020,2021],{"class":1996}," # Git section (git_branch + git_status)\n",[51,2023,2024,2027],{"class":53,"line":250},[51,2025,2026],{"class":103}," node",[51,2028,2029],{"class":1996}," # Node.js section\n",[51,2031,2032,2035],{"class":53,"line":256},[51,2033,2034],{"class":103}," docker",[51,2036,2037],{"class":1996}," # Docker section\n",[51,2039,2040,2043],{"class":53,"line":262},[51,2041,2042],{"class":103}," exec_time",[51,2044,2045],{"class":1996}," # Execution time\n",[51,2047,2048,2051],{"class":53,"line":272},[51,2049,2050],{"class":103}," line_sep",[51,2052,2053],{"class":1996}," # Line break\n",[51,2055,2056,2059],{"class":53,"line":282},[51,2057,2058],{"class":103}," jobs",[51,2060,2061],{"class":1996}," # Background jobs indicator\n",[51,2063,2064,2067],{"class":53,"line":294},[51,2065,2066],{"class":103}," exit_code",[51,2068,2069],{"class":1996}," # Exit code section\n",[51,2071,2072,2075],{"class":53,"line":300},[51,2073,2074],{"class":103}," char",[51,2076,2077],{"class":1996}," # Prompt character\n",[51,2079,2080],{"class":53,"line":307},[51,2081,202],{"class":65},[18,2083,2084],{},"und auch das Symbol mit welchem eine Zeile beginnen soll. Ich wollte weiterhin das Zeichen der pure prompt verwenden und\nhabe daher den default überschrieben:",[42,2086,2088],{"className":44,"code":2087,"language":46,"meta":47,"style":47},"SPACESHIP_CHAR_SYMBOL='❯ '\n",[22,2089,2090],{"__ignoreMap":47},[51,2091,2092,2095,2097],{"class":53,"line":54},[51,2093,2094],{"class":65},"SPACESHIP_CHAR_SYMBOL",[51,2096,75],{"class":57},[51,2098,2099],{"class":103},"'❯ '\n",[523,2101,1832],{"id":2102},"git",[18,2104,2105,2106,858,2109,2112,2113,146,2118,146,2123,146,2128,2133],{},"Git verwende ich überwiegend über die Konsole. Mit grafischen Programmen bin ich nie richtig warm geworden. Angefangen\nmit ",[22,2107,2108],{},"gitk",[22,2110,2111],{},"git gui"," auf einem Linux Rechner bin ich\nüber ",[540,2114,2117],{"href":2115,"rel":2116},"https://git-cola.github.io/",[544],"git-cola",[540,2119,2122],{"href":2120,"rel":2121},"https://www.sourcetreeapp.com/",[544],"SourceTree",[540,2124,2127],{"href":2125,"rel":2126},"https://www.gitkraken.com/",[544],"GitKraken",[540,2129,2132],{"href":2130,"rel":2131},"https://git-fork.com/",[544],"Fork","\nund noch ein paar andere gestolpert. Aber das Geklicke und der Kontextwechsel zwischen IDE und $Werkzeug haben mich\nimmer gestört. Auch visuell fand ich bis auf GitKraken und neuerdings Fork nichts ansprechend (ja, ist mir wichtig, hat\nman anfangs schon beim Dark Theme fürs Terminal bemerkt 🙄). Hauptsächlich hat mich aber das Nichtvorhandensein bzw. die\numständliche Handhabung des rebasen gestört. Das geht für mich am schnellsten auf der Konsole mit eigenen git aliasen.",[18,2135,2136],{},"Meine häufigsten Befehle sind:",[903,2138,2139,2156,2169,2182,2199],{},[906,2140,2141,2144],{},[22,2142,2143],{},"git up",[903,2145,2146,2153],{},[906,2147,2148,2149,2152],{},"hole den aktuellen Stand von ",[22,2150,2151],{},"remote"," mit einem rebase",[906,2154,2155],{},"lösche alle lokalen Branches die bereits gemerged wurden",[906,2157,2158,2161,2166,2168],{},[22,2159,2160],{},"git cm \"jetzt funktionierts wirklich!!!1elf\"",[903,2162,2163],{},[906,2164,2165],{},"comitte alle Änderungen mit der folgenden commit message",[1934,2167],{},"(inklusive neu angelegte Dateien)",[906,2170,2171,2174,2179,2181],{},[22,2172,2173],{},"git amend",[903,2175,2176],{},[906,2177,2178],{},"füge alle Änderungen dem letzten HEAD commit hinzu",[1934,2180],{},"(exklusive neu angelegte Dateien)",[906,2183,2184,2187],{},[22,2185,2186],{},"git ll",[903,2188,2189,2192],{},[906,2190,2191],{},"drucke eine Liste aller commit hashes und messages des aktuellen Branches ab HEAD",[906,2193,2194,2195,2198],{},"die Länge der Liste kann mit ",[22,2196,2197],{},"-42"," begrenzt werden",[906,2200,2201,858,2204,2207],{},[22,2202,2203],{},"git fix",[22,2205,2206],{},"git ri",[903,2208,2209,2216],{},[906,2210,2211,2212,2215],{},"erzeuge einen ",[22,2213,2214],{},"fixup"," commit und räume die commits mit einem interactive rebase auf",[906,2217,2218],{},"workflow ist dann wie folgt:",[42,2220,2222],{"className":44,"code":2221,"language":46,"meta":47,"style":47},"❯ git ll -5\na52747d (HEAD -> master, origin/master, origin/HEAD) foo\nb858f05 bar\n4954b3d baz\n782d959 bum\n2b1c7c6 buff\n❯ git add .\n❯ git fix 782d959\n❯ git ri 782d959^\n",[22,2223,2224,2238,2261,2269,2277,2285,2293,2305,2317],{"__ignoreMap":47},[51,2225,2226,2229,2232,2235],{"class":53,"line":54},[51,2227,2228],{"class":61},"❯",[51,2230,2231],{"class":103}," git",[51,2233,2234],{"class":103}," ll",[51,2236,2237],{"class":96}," -5\n",[51,2239,2240,2243,2246,2249,2252,2255,2258],{"class":53,"line":69},[51,2241,2242],{"class":61},"a52747d",[51,2244,2245],{"class":65}," (HEAD -",[51,2247,2248],{"class":57},">",[51,2250,2251],{"class":103}," master,",[51,2253,2254],{"class":103}," origin/master,",[51,2256,2257],{"class":103}," origin/HEAD",[51,2259,2260],{"class":65},") foo\n",[51,2262,2263,2266],{"class":53,"line":82},[51,2264,2265],{"class":61},"b858f05",[51,2267,2268],{"class":103}," bar\n",[51,2270,2271,2274],{"class":53,"line":205},[51,2272,2273],{"class":61},"4954b3d",[51,2275,2276],{"class":103}," baz\n",[51,2278,2279,2282],{"class":53,"line":241},[51,2280,2281],{"class":61},"782d959",[51,2283,2284],{"class":103}," bum\n",[51,2286,2287,2290],{"class":53,"line":250},[51,2288,2289],{"class":61},"2b1c7c6",[51,2291,2292],{"class":103}," buff\n",[51,2294,2295,2297,2299,2302],{"class":53,"line":256},[51,2296,2228],{"class":61},[51,2298,2231],{"class":103},[51,2300,2301],{"class":103}," add",[51,2303,2304],{"class":103}," .\n",[51,2306,2307,2309,2311,2314],{"class":53,"line":262},[51,2308,2228],{"class":61},[51,2310,2231],{"class":103},[51,2312,2313],{"class":103}," fix",[51,2315,2316],{"class":103}," 782d959\n",[51,2318,2319,2321,2323,2326],{"class":53,"line":272},[51,2320,2228],{"class":61},[51,2322,2231],{"class":103},[51,2324,2325],{"class":103}," ri",[51,2327,2328],{"class":103}," 782d959^\n",[18,2330,2331,2332,2335],{},"Eigene git aliase können in der globalen git config unter ",[22,2333,2334],{},"~/.gitconfig"," abgelegt werden.",[18,2337,2338,2339,2342],{},"Da ich meine merge requests im Normalfall erst auf den master rebase bevor ich sie zum mergen freigebe, bin ich super\nglücklich über das ",[22,2340,2341],{},"rerere"," Feature von git. Ist das Feature aktiviert, merkt sich git beim rebasen, wie ein Konflikt\ngelöst wird. Kommt es erneut zu dem selben Konflikt, weiß git wie es diesen automatisiert zu lösen hat.",[42,2344,2346],{"className":44,"code":2345,"language":46,"meta":47,"style":47},"[rerere]\n enabled = true\n[alias]\n s = status\n st = stash\n co = checkout\n cob = checkout -b\n fix = commit --fixup\n ri = rebase -i --autosquash\n up = !git pull --rebase --prune && git bclean\n cm = !git add -A && git commit -m\n amend = commit -a --amend\n ll = !git --no-pager log --oneline --decorate\n bclean = \"!f() { branches=$(git branch --merged ${1-master} | grep -v \" ${1-master}$\"); [ -z \\\"$branches\\\" ] || git branch -d$branches; }; f\"\n",[22,2347,2348,2353,2364,2369,2379,2389,2399,2412,2425,2441,2468,2491,2506,2527],{"__ignoreMap":47},[51,2349,2350],{"class":53,"line":54},[51,2351,2352],{"class":65},"[rerere]\n",[51,2354,2355,2358,2361],{"class":53,"line":69},[51,2356,2357],{"class":61}," enabled",[51,2359,2360],{"class":103}," =",[51,2362,2363],{"class":96}," true\n",[51,2365,2366],{"class":53,"line":82},[51,2367,2368],{"class":65},"[alias]\n",[51,2370,2371,2374,2376],{"class":53,"line":205},[51,2372,2373],{"class":61}," s",[51,2375,2360],{"class":103},[51,2377,2378],{"class":103}," status\n",[51,2380,2381,2384,2386],{"class":53,"line":241},[51,2382,2383],{"class":61}," st",[51,2385,2360],{"class":103},[51,2387,2388],{"class":103}," stash\n",[51,2390,2391,2394,2396],{"class":53,"line":250},[51,2392,2393],{"class":61}," co",[51,2395,2360],{"class":103},[51,2397,2398],{"class":103}," checkout\n",[51,2400,2401,2404,2406,2409],{"class":53,"line":256},[51,2402,2403],{"class":61}," cob",[51,2405,2360],{"class":103},[51,2407,2408],{"class":103}," checkout",[51,2410,2411],{"class":96}," -b\n",[51,2413,2414,2417,2419,2422],{"class":53,"line":262},[51,2415,2416],{"class":61}," fix",[51,2418,2360],{"class":103},[51,2420,2421],{"class":103}," commit",[51,2423,2424],{"class":96}," --fixup\n",[51,2426,2427,2430,2432,2435,2438],{"class":53,"line":272},[51,2428,2429],{"class":61}," ri",[51,2431,2360],{"class":103},[51,2433,2434],{"class":103}," rebase",[51,2436,2437],{"class":96}," -i",[51,2439,2440],{"class":96}," --autosquash\n",[51,2442,2443,2446,2448,2451,2454,2457,2460,2463,2465],{"class":53,"line":282},[51,2444,2445],{"class":61}," up",[51,2447,2360],{"class":103},[51,2449,2450],{"class":103}," !git",[51,2452,2453],{"class":103}," pull",[51,2455,2456],{"class":96}," --rebase",[51,2458,2459],{"class":96}," --prune",[51,2461,2462],{"class":65}," && ",[51,2464,2102],{"class":61},[51,2466,2467],{"class":103}," bclean\n",[51,2469,2470,2473,2475,2477,2479,2482,2484,2486,2488],{"class":53,"line":294},[51,2471,2472],{"class":61}," cm",[51,2474,2360],{"class":103},[51,2476,2450],{"class":103},[51,2478,2301],{"class":103},[51,2480,2481],{"class":96}," -A",[51,2483,2462],{"class":65},[51,2485,2102],{"class":61},[51,2487,2421],{"class":103},[51,2489,2490],{"class":96}," -m\n",[51,2492,2493,2496,2498,2500,2503],{"class":53,"line":300},[51,2494,2495],{"class":61}," amend",[51,2497,2360],{"class":103},[51,2499,2421],{"class":103},[51,2501,2502],{"class":96}," -a",[51,2504,2505],{"class":96}," --amend\n",[51,2507,2508,2511,2513,2515,2518,2521,2524],{"class":53,"line":307},[51,2509,2510],{"class":61}," ll",[51,2512,2360],{"class":103},[51,2514,2450],{"class":103},[51,2516,2517],{"class":96}," --no-pager",[51,2519,2520],{"class":103}," log",[51,2522,2523],{"class":96}," --oneline",[51,2525,2526],{"class":96}," --decorate\n",[51,2528,2529,2532,2534,2537,2539,2542,2545,2548,2551,2554,2557,2559,2562,2565,2568,2571,2573,2575,2577,2580,2582,2585,2587,2590,2592],{"class":53,"line":355},[51,2530,2531],{"class":61}," bclean",[51,2533,2360],{"class":103},[51,2535,2536],{"class":103}," \"!f() { branches=$(",[51,2538,2102],{"class":61},[51,2540,2541],{"class":103}," branch ",[51,2543,2544],{"class":96},"--merged",[51,2546,2547],{"class":96}," ${1",[51,2549,2550],{"class":103},"-",[51,2552,2553],{"class":65},"master",[51,2555,2556],{"class":96},"}",[51,2558,230],{"class":57},[51,2560,2561],{"class":61}," grep",[51,2563,2564],{"class":96}," -v",[51,2566,2567],{"class":103}," \" ",[51,2569,2570],{"class":96},"${1",[51,2572,2550],{"class":103},[51,2574,2553],{"class":65},[51,2576,2556],{"class":96},[51,2578,2579],{"class":103},"$\"); [ -z ",[51,2581,132],{"class":96},[51,2583,2584],{"class":65},"$branches",[51,2586,132],{"class":96},[51,2588,2589],{"class":103}," ] || git branch -d",[51,2591,2584],{"class":65},[51,2593,2594],{"class":103},"; }; f\"\n",[523,2596,1839],{"id":2597},"programme",[18,2599,2600],{},"Weitere Programme, die ich in meiner täglichen Arbeit nicht mehr missen möchte, sind:",[903,2602,2603,2626,2644,2658,2691,2712,2733],{},[906,2604,2605,2610,2611,2614],{},[540,2606,2609],{"href":2607,"rel":2608},"https://brew.sh/",[544],"homebrew"," ",[910,2612,2613],{},"(kommandozeile)",[903,2615,2616,2619],{},[906,2617,2618],{},"quasi DER Paketmanager für OSX",[906,2620,2621,2622,2625],{},"Programme installieren mit ",[22,2623,2624],{},"brew install FOO",". Schneller gehts nicht!",[906,2627,2628,2610,2633,2635],{},[540,2629,2632],{"href":2630,"rel":2631},"https://github.com/direnv/direnv",[544],"direnv",[910,2634,2613],{},[903,2636,2637],{},[906,2638,2639,2640,2643],{},"lege in einem Verzeichnis eine ",[22,2641,2642],{},".envrc"," Datei ab und definiere dort z. B. die Java oder NodeJS Version, oder auch\neine andere git E-Mail-Adresse, falls man hier eine andere als die globale verwenden möchte für commits",[906,2645,2646,2610,2651,2653],{},[540,2647,2650],{"href":2648,"rel":2649},"https://stedolan.github.io/jq/",[544],"jq",[910,2652,2613],{},[903,2654,2655],{},[906,2656,2657],{},"JSON Prozessor für die Kommandozeile",[906,2659,2660,2610,2665,2667],{},[540,2661,2664],{"href":2662,"rel":2663},"https://github.com/nodenv/nodenv",[544],"nodenv",[910,2666,2613],{},[903,2668,2669,2678],{},[906,2670,2671,2672,2677],{},"verwalte unterschiedliche Versionen von NodeJS (Alternative zu z.\nB. ",[540,2673,2676],{"href":2674,"rel":2675},"https://github.com/creationix/nvm",[544],"Node Version Manager (nvm)",")",[906,2679,2680,2681,2684,2685,2687,2688,2690],{},"Anfang des Jahres bin ich von ",[22,2682,2683],{},"nvm"," auf ",[22,2686,2664],{}," umgestiegen, weil letzteres die Shell beim Initialisieren nicht\nblockiert. ",[22,2689,2683],{}," braucht seine Zeit zum Laden von NodeJS",[906,2692,2693,2698],{},[540,2694,2697],{"href":2695,"rel":2696},"https://www.spectacleapp.com/",[544],"spectacle",[903,2699,2700,2703,2706,2709],{},[906,2701,2702],{},"verschiebe Fenster und ändere deren Größe mit deiner Tastatur",[906,2704,2705],{},"die Tastenkombinationen sind konfigurierbar",[906,2707,2708],{},"ich hatte einen UltraWide Monitor und habe so schnell mal drei Fenster in der Breite gedrittelt verteilt",[906,2710,2711],{},"Natürlich ist das auch auf einem normalen Laptop Display sinnvoll, Fenster ohne Maus rechts/links oder oben/unten\nausrichten zu können",[906,2713,2714,2719],{},[540,2715,2718],{"href":2716,"rel":2717},"https://github.com/Clipy/Clipy",[544],"clipy",[903,2720,2721,2730],{},[906,2722,2723,2724,2729],{},"wer die copy-paste aus IntelliJ lieb gewonnen hat; clipy macht das systemweit (die Alternative wäre, das\nPowerpack für ",[540,2725,2728],{"href":2726,"rel":2727},"https://www.alfredapp.com/",[544],"Alfred"," zu kaufen)",[906,2731,2732],{},"man achte auf Passwörter! Clipy bietet die Möglichkeit, bestimmte Programme zu ignorieren. Es ist ratsam dort\nKeepassX oder sonstigen Tresor einzutragen 😉",[906,2734,2735,2738],{},[1030,2736,2737],{},"spotify",[903,2739,2740],{},[906,2741,2742],{},"Musik muss natürlich sein 🎧",[18,2744,2745],{},"Vielen Dank fürs Lesen (☞゚ヮ゚)☞ (smilie eingefügt mit clipy snippets)",[799,2747,2748],{},"html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}",{"title":47,"searchDepth":69,"depth":69,"links":2750},[2751,2752,2753],{"id":1842,"depth":69,"text":1825},{"id":2102,"depth":69,"text":1832},{"id":2597,"depth":69,"text":1839},[810,811],"2018-11-09T12:41:34","Beim synyx Camp vor zwei Wochen haben wir uns unter anderem über das Setup unserer Entwicklungsumgebungen unterhalten.\\nIm Folgenden möchte ich kurz berichten, wie ich meine eingerichtet habe und welche Programme ich in meiner alltäglichen\\nArbeit nicht mehr missen möchte.","https://synyx.de/blog/wie-meine-entwicklungsumgebung-eingerichtet-ist/",{},"/blog/wie-meine-entwicklungsumgebung-eingerichtet-ist",{"title":1804,"description":1813},"blog/wie-meine-entwicklungsumgebung-eingerichtet-ist",[1570],"Beim synyx Camp vor zwei Wochen haben wir uns unter anderem über das Setup unserer Entwicklungsumgebungen unterhalten. Im Folgenden möchte ich kurz berichten, wie ich meine eingerichtet habe und welche Programme ich in meiner alltäglichen Arbeit nicht mehr missen möchte.","38z-kKuLlmfwcQfMnzrrf1gJHoh-qvNZsZ_skAURUrY",{"id":2766,"title":2767,"author":2768,"body":2770,"category":2926,"date":2927,"description":2928,"extension":815,"link":2929,"meta":2930,"navigation":303,"path":2931,"seo":2932,"slug":2774,"stem":2933,"tags":2934,"teaser":2939,"__hash__":2940},"blog/blog/how-we-ended-up-using-bdd.md","How we ended up using BDD",[2769],"weigel",{"type":11,"value":2771,"toc":2919},[2772,2775,2778,2782,2785,2789,2792,2796,2799,2803,2844,2850,2856,2862,2868,2872,2875,2884,2887],[14,2773,2767],{"id":2774},"how-we-ended-up-using-bdd",[18,2776,2777],{},"It was not our primary goal to use Behaviour Driven Development (BDD) in the project at a customer, but while finding\nand optimizing our agile software development process we recognized that we established the building blocks of BDD. It\nworks quite well and offers a lot of space and flexibility for our future plans, switching our architecture to\nmicroservices.",[1503,2779,2781],{"id":2780},"the-project-setup","The project setup",[18,2783,2784],{},"But let us start at the beginning of the project. We started on a green field project but had to embed in a system\ncontext with established interfaces and many third party services as we had to replace a legacy system. Also as a newly\nformed team consisting of internal and external employees we had to find our team spirit and embed into the existing\norganizational structure. With at least one dedicated Product Owner (PO) and a dedicated Scrum Master we started as a\nteam of 7 people. We are one of multiple Scrum teams in an organization sector. The operations team is (still) in\nanother sector and the domain experts or Functional Owner (FO) even in another building. On the one hand it makes\ncommunication on domain topics harder, on the other hand with this setting we had a lot of liberties to build, release\nand deploy whenever we want. Noticing that other teams organized their cyclic release as part of their scrum process\nevery two weeks our way to deploy after feature complete, seemed to be unconventional to others.",[1503,2786,2788],{"id":2787},"trust-as-prerequisite-to-keep-liberties-in-development-process","Trust as prerequisite to keep liberties in development process",[18,2790,2791],{},"As time went by we had our first deployment to production. We have been consuming other services and vice versa. While\nmore features were added, the complexity and the amount of deployments to production increased. Our POs’ confidence in\nour unconventional release and deploy process decreased, perhaps feeling that the project got out of hand. Of course we\nsent announcement E-Mails to relevant stakeholders, but somehow we had not built up enough trust yet and our POs still\nhad the desire to make manual tests on the release stage. So we started to create charts of all processes together with\nour POs as part of our development process just before talking about the user story in our refinement. It was and still\nis helpful for two reasons. On the one hand it was creating a format the whole team understands as it was simplifying\nthe process and serving as documentation. On the other hand it was serving as kind of a contract, which we as devs and\nthe POs were committed to. Also we arranged that our POs wrote acceptance tests for the use case. With those newly\nintroduced methods in our development process, confidence and trust increased and we kept our way of releasing and\ndeploying after the feature is “Definition of Done” ready during the sprint.",[1503,2793,2795],{"id":2794},"tests-turn-from-verification-to-specification","Tests turn from verification to specification",[18,2797,2798],{},"Again time went by and as the FOs are sitting in another building and in contrast to our POs are feeling the need of\nmanual tests, again the question for a fixed deployment-plan arose. Newly implemented features and more dependencies to\nother teams strengthened the need. To hold on to our flexibility and to keep deploying during our sprint we introduced a\nprocess called “3 Amigo Testing”. Before a story accomplishes the “Definition of Ready” and is thereby ready for our\nrefinement the tests have to be written together with our FOs, our POs and us (one of the devs). Thereby tests turn from\nverification to specification no matter at which level: unit, integration or system integration! In addition the\ndiscussion not only improves the communication between the business and the technical team, it also helps to understand\nthe complexity and the pitfalls early which results in a better estimation.",[1503,2800,2802],{"id":2801},"time-to-grasp-the-nettle","Time to grasp the nettle",[18,2804,2805,2806,2811,2812,2817,2818,2610,2823,2610,2828,2610,2833,2610,2838,2843],{},"Technically that means you need an abstraction on top of your automated tests. Behaviour tests should always be written\nfrom a customer’s point of view and describe the acceptance criteria of a use case. Important is, that you use a\nbusiness readable language like ",[540,2807,2810],{"href":2808,"rel":2809},"https://github.com/cucumber/cucumber/wiki/Gherkin",[544],"gherkin",". It describes a test in the\nform of an initial state (“given”), an action (“when”) and a final state (“then”) which is common in the BDD context. By\nthat you have a description of a finite state machine which gives you the ability to describe a feature integrally. It\ndoes not matter at which level of the testing pyramide the actual implementation of the test is settled. A defined\nrequirement may be implemented at unit level as well as at system integration level. We\nuse ",[540,2813,2816],{"href":2814,"rel":2815},"https://cucumber.io/",[544],"cucumber"," because it meets the aforementioned requirements and because of existing knowledge\nand easy integration in our ecosystem. It is easy to use with the spring framework and gives us pretty jenkins\nreportings loved by our POs. But it is\nonly ",[540,2819,2822],{"href":2820,"rel":2821},"http://lettuce.it/",[544],"one",[540,2824,2827],{"href":2825,"rel":2826},"http://spockframework.org/",[544],"of",[540,2829,2832],{"href":2830,"rel":2831},"http://robotframework.org/",[544],"many",[540,2834,2837],{"href":2835,"rel":2836},"http://jbehave.org/",[544],"possible",[540,2839,2842],{"href":2840,"rel":2841},"http://www.thucydides.info/#/",[544],"tools",".\nFinally we recognized, that we established the building blocks of BDD:",[18,2845,2846,2849],{},[1030,2847,2848],{},"Define tests beforehand"," – The specification of tests takes place before talking about the story in the refinement.",[18,2851,2852,2855],{},[1030,2853,2854],{},"Use ubiquitous language"," – Defining the behaviour together with our POs and FOs we use a common language to avoid\nmisunderstandings.",[18,2857,2858,2861],{},[1030,2859,2860],{},"Express all requirements in high-level business terms from the customer point of view"," – The tests serve as the\nacceptance criteria of the story describing the next business value to deliver. Therefore it is defined from the\ncustomer’s perspective.",[18,2863,2864,2867],{},[1030,2865,2866],{},"Accessible to all stakeholders"," – The tests, which now serve as documentation, are accessible to all stakeholders via\na reporting plugin provided by our continuous integration tool.",[1503,2869,2871],{"id":2870},"conclusion","Conclusion",[18,2873,2874],{},"With that said we built up not only the technical prerequisite to eliminate all manual tests but also gained trust and\nare now ready for continuous deployment. In addition our testsuite turns out to be a helpful backbone for our ongoing\nproject of splitting a monolith into microservices.",[18,2876,2877,2878,2883],{},"If u want to know more about the project join us for our ",[540,2879,2882],{"href":2880,"rel":2881},"https://synyx.de/events/17_08_jugka_microservices_andi/",[544],"talk","\nat Java User Group Karlsruhe!",[18,2885,2886],{},"Sources:",[903,2888,2889,2895,2901,2907,2913],{},[906,2890,2891],{},[540,2892,2893],{"href":2893,"rel":2894},"https://www.agilealliance.org/glossary/bdd",[544],[906,2896,2897],{},[540,2898,2899],{"href":2899,"rel":2900},"https://dannorth.net/introducing-bdd/",[544],[906,2902,2903],{},[540,2904,2905],{"href":2905,"rel":2906},"https://martinfowler.com/bliki/GivenWhenThen.html",[544],[906,2908,2909],{},[540,2910,2911],{"href":2911,"rel":2912},"http://www.agiletestingframework.com/atf/testing/behavior-driven-development-bdd/",[544],[906,2914,2915],{},[540,2916,2917],{"href":2917,"rel":2918},"https://en.wikipedia.org/wiki/Behavior-driven_development",[544],{"title":47,"searchDepth":69,"depth":69,"links":2920},[2921,2922,2923,2924,2925],{"id":2780,"depth":82,"text":2781},{"id":2787,"depth":82,"text":2788},{"id":2794,"depth":82,"text":2795},{"id":2801,"depth":82,"text":2802},{"id":2870,"depth":82,"text":2871},[810,811],"2017-07-31T14:13:17","It was not our primary goal to use Behaviour Driven Development (BDD) in the project at a customer, but while finding\\nand optimizing our agile software development process we recognized that we established the building blocks of BDD. It\\nworks quite well and offers a lot of space and flexibility for our future plans, switching our architecture to\\nmicroservices.","https://synyx.de/blog/how-we-ended-up-using-bdd/",{},"/blog/how-we-ended-up-using-bdd",{"title":2767,"description":2777},"blog/how-we-ended-up-using-bdd",[2935,2936,1570,2937,2938],"bdd","behaviour","driven","trust","It was not our primary goal to use Behaviour Driven Development (BDD) in the project at a customer, but while finding and optimizing our agile software development process we recognized that we established the building blocks of BDD. It works quite well and offers a lot of space and flexibility for our future plans, switching our architecture to microservices. The project setup But let us start at the beginning of the project.","yImgzxEzR7UHl6fIsRCGniHr2xISKHBeIlBhI5IqQOs",{"id":2942,"title":2943,"author":2944,"body":2946,"category":3125,"date":3126,"description":3127,"extension":815,"link":3128,"meta":3129,"navigation":303,"path":3130,"seo":3131,"slug":2950,"stem":3133,"tags":3134,"teaser":3139,"__hash__":3140},"blog/blog/validating-internal-structure-dependencies-using-intellij-idea.md","Validating internal structure / dependencies using IntelliJ IDEA",[2945],"kannegiesser",{"type":11,"value":2947,"toc":3123},[2948,2951,2978,3004,3007,3021,3032,3038,3056,3062,3065,3072,3078,3081,3092,3098,3101,3107,3115],[14,2949,2943],{"id":2950},"validating-internal-structure-dependencies-using-intellij-idea",[18,2952,2953,2954,2959,2960,2965,2966,2971,2972,2977],{},"There are several different tools to maintain the internal structure of a java application available. The tools range\nfrom simple open source software like",[540,2955,2958],{"href":2956,"rel":2957},"https://github.com/clarkware/jdepend",[544],"jdepend","\nand ",[540,2961,2964],{"href":2962,"rel":2963},"https://web.archive.org/web/20200924121825/http://blog.schauderhaft.de/degraph/",[544],"degraph"," to full fledged\narchitecture tooling like ",[540,2967,2970],{"href":2968,"rel":2969},"http://structure101.com/",[544],"Structure101","\nor ",[540,2973,2976],{"href":2974,"rel":2975},"https://www.hello2morrow.com/products/sonargraph/architect9",[544],"Sonargraph Architect",". All these provide methods to\ndefine the internal structure of an application and validate it somehow.",[18,2979,2980,2981,2986,2987,2992,2993,2998,2999,1250],{},"Since we are using ",[540,2982,2985],{"href":2983,"rel":2984},"https://www.jetbrains.com/idea/",[544],"IntelliJ IDEA"," in many of our teams I’d like to show a handy little\nfeature of the IDE that helps in maintaining a structured application:In IDEA you\ncan ",[540,2988,2991],{"href":2989,"rel":2990},"https://www.jetbrains.com/help/idea/2017.1/scopes.html",[544],"define scopes"," and match your code to them. You can then\nrule how they may or may not access each other using\nthe ",[540,2994,2997],{"href":2995,"rel":2996},"https://www.jetbrains.com/help/idea/2017.1/dependency-viewer.html",[544],"Dependency Viewer",". As soon as you do this the\nIDE warns you about illegal package access just as you type. Note that the shown features currently work in the\nfree ",[540,3000,3003],{"href":3001,"rel":3002},"https://www.jetbrains.com/idea/#chooseYourEdition",[544],"IntelliJ IDEA Community Edition",[18,3005,3006],{},"Imagine we have a simple layered application with Java packages representing these layers:",[903,3008,3009,3012,3015,3018],{},[906,3010,3011],{},"api: API-Layer features controllers that are responsible to render a RESTful API to our application",[906,3013,3014],{},"business: our higher level operations/business logic happens here",[906,3016,3017],{},"persistence: this layer is responsible for storing data and providing access to it",[906,3019,3020],{},"domain: In addition we have a domain “layer” where all the layers share their common domain",[18,3022,3023,3024,3027,3028,3031],{},"We can now define scopes for each of these layers. We can do so using ",[910,3025,3026],{},"File -> Settings"," and then select\n",[910,3029,3030],{},"Appearance & Behaviour -> Scopes",". Here we can add new scopes and assign files to it using a pattern or by\nnavigating to the corresponding packages and include/exclude them.",[18,3033,3034],{},[960,3035],{"alt":3036,"src":3037},"\"Creating Scopes using Settings\"","https://media.synyx.de/uploads//2017/05/1-creating-sopes.png",[18,3039,3040,3041,3044,3045,3048,3049,3052,3053],{},"We then define archuitectural constraints on these scopes using the ",[910,3042,3043],{},"Analyze Dependencies View"," (accessible using the\n",[910,3046,3047],{},"Analyze"," Menu). We do so by clicking the ",[910,3050,3051],{},"Edit Rules Icon"," in the ",[910,3054,3055],{},"Dependency-View.",[18,3057,3058],{},[960,3059],{"alt":3060,"src":3061},"\"Defining rules\"","https://media.synyx.de/uploads//2017/05/2-defining-rules.png",[18,3063,3064],{},"In the example above configured simple constraints for our layered architecture: We only allow access between layers\nfrom an upper layer to a lower layer (API to Business and Business to Persistence) and we dont allow Domain to access\nany other layer.",[18,3066,3067,3068,3071],{},"When we return to the ",[910,3069,3070],{},"Dependency-View"," and re-run the analysis it displays all the violations of our architecture.\nAlso, we get the analysis as soon as we type a new Validation in the Editor.",[18,3073,3074],{},[960,3075],{"alt":3076,"src":3077},"\"Violations view from dependencies\"","https://media.synyx.de/uploads//2017/05/3-violations-in-dependency-view.png",[18,3079,3080],{},"You can see in the screenshot that we have an illegal access from a class in scope Persistence that accesses something\nin scope Business. We can simply fix this by moving the Initializer to the business-Package and the error disappears.",[18,3082,3083,3084,3087,3088,3091],{},"As an alternative we can also access the information by running the analysis ",[910,3085,3086],{},"Illegal package dependencies"," (e.g. using\nthe ",[910,3089,3090],{},"Analyze – > Run inspection by name",") dialog. From there we can also edit our rules and navigate to the violating\ncode.",[18,3093,3094],{},[960,3095],{"alt":3096,"src":3097},"\"Violations-View from inspections\"","https://media.synyx.de/uploads//2017/05/4-violations-in-inspections-view.png",[18,3099,3100],{},"If you enable the shared option for scopes IDEA will also write the configuration to .idea/scopes from where you can\nshare them with your team members.",[18,3102,3103],{},[960,3104],{"alt":3105,"src":3106},"\"Scope definitions in .idea/scopes\"","https://media.synyx.de/uploads//2017/05/5-export.png",[18,3108,3109,3110,1250],{},"To test and experiment with scopes yourself you can build on\nmy",[540,3111,3114],{"href":3112,"rel":3113},"https://github.com/marckanneg/idea-scopes-demo",[544],"my demo-project",[18,3116,3117,3118,3122],{},"You can clone it\nfrom ",[540,3119,3120],{"href":3120,"rel":3121},"https://github.com/marckanneg/idea-scopes-demo.git",[544]," and open\nit using your IntelliJ IDEA(File ->Open).",{"title":47,"searchDepth":69,"depth":69,"links":3124},[],[810],"2017-05-16T12:18:36","There are several different tools to maintain the internal structure of a java application available. The tools range\\nfrom simple open source software likejdepend\\nand degraph to full fledged\\narchitecture tooling like Structure101\\nor Sonargraph Architect. All these provide methods to\\ndefine the internal structure of an application and validate it somehow.","https://synyx.de/blog/validating-internal-structure-dependencies-using-intellij-idea/",{},"/blog/validating-internal-structure-dependencies-using-intellij-idea",{"title":2943,"description":3132},"There are several different tools to maintain the internal structure of a java application available. The tools range\nfrom simple open source software likejdepend\nand degraph to full fledged\narchitecture tooling like Structure101\nor Sonargraph Architect. All these provide methods to\ndefine the internal structure of an application and validate it somehow.","blog/validating-internal-structure-dependencies-using-intellij-idea",[3135,3136,828,3137,3138],"idea","layers","software-qualitat","softwarearchitektur","There are several different tools to maintain the internal structure of a java application available. The tools range from simple open source software like jdepend and degraph to full fledged architecture…","nRJYUHMxI944vdsZA69SvSdJ70YYEvHqera1wPQeKgI",{"id":3142,"title":3143,"author":3144,"body":3146,"category":3300,"date":3301,"description":47,"extension":815,"link":3302,"meta":3303,"navigation":303,"path":3304,"seo":3305,"slug":3306,"stem":3307,"tags":3308,"teaser":3311,"__hash__":3312},"blog/blog/von-profis-lernen-heisst-siegen-lernen.md","Von Profis lernen heißt siegen lernen",[836,3145],"ulrich",{"type":11,"value":3147,"toc":3291},[3148,3151,3174,3177,3190,3193,3196,3200,3203,3206,3210,3213,3216,3220,3223,3226,3229,3233,3236,3239,3243,3246,3252,3255,3259,3262,3265,3269,3272,3277,3280,3285,3288],[14,3149,3143],{"id":3150},"von-profis-lernen-heißt-siegen-lernen",[3152,3153,3154,3159,3164,3169],"blockquote",{},[18,3155,3156],{},[910,3157,3158],{},"Der nächste Sprint steht an… Wir treffen uns am Dienstag Morgen um 10:00 Uhr zum Refinement. Es gilt noch einige\nThemen fachlich genauer zu hinterfragen. Auch technische Grundlagen müssen noch geklärt werden.",[18,3160,3161],{},[910,3162,3163],{},"Abends geht’s zum Fußballtraining. Der Trainer hält erst einmal einen 15 minütigen Vortrag über das letzte Spiel am\nSonntag. Dann fängt er auch noch an über das nächste Spiel zu reden wie wichtig das sei… Ich will doch nur kicken.\nVerteil endlich die Leibchen!",[18,3165,3166],{},[910,3167,3168],{},"Mittwoch morgen unter der Dusche gehe ich nochmal die technischen Themen des Refinements durch. Mir fällt auf, dass\nunser Ansatz wohl nur bedingt funktioniert und habe eine alternative Idee. Glück gehabt… Man wär das ärgerlich gewesen\ndas erst zu bemerken, nachdem ein Tag Entwicklung investiert wurde. Im Büro angekommen setze ich mir erst einmal eine\nTimebox von einer Stunde und evaluiere eine Bibliothek.",[18,3170,3171],{},[910,3172,3173],{},"Donnerstag dann wieder Fußballtraining. Der Trainer kommt später. Steckt noch im Stau bekomme ich in der Kabine\ngesagt. Wir krallen uns die Bälle, legen sie auf die Strafraumlinie und ballern das Runde ins Eckige. Als der Trainer\ndann kommt machen wir zum Aufwärmen wieder dieses Passspiel-Laufweg-Dingens. Als ob das was bringen würde.",[18,3175,3176],{},"“Von Sportlern lernen heißt siegen lernen”. Dieser Spruch ist mehr oder weniger jedem geläufig. Die einleitende\nAnekdote löst bei so mancher Person vielleicht noch weitere Erinnerungen aus. Aus diesem Winkel betrachtet, erfordert\nder eben erwähnte Spruch vermutlich eine Überholung. Viel treffender wäre an dieser Stelle: “Vom Berufsleben lernen\nheißt siegen lernen”.",[18,3178,3179,3180,3183,3184,3186,3187,3189],{},"Das ",[910,3181,3182],{},"ich"," ist froh nicht gleich in die Tastatur gehauen, sondern vorerst geplant zu haben. Das ",[910,3185,3182],{}," setzt sich\ndiszipliniert eine Timebox um neue Technologien zu evaluieren. Das selbe ",[910,3188,3182],{}," jedoch, will “einfach nur kicken”,\nanstatt das eigene Spiel und das des Teams mit spezifischen Trainingseinheiten zu verbessern.",[18,3191,3192],{},"Vergleicht man die Sportart Fussball mit dem Beruf der Softwareentwicklung, zeigen sich interessante Analogien auf. Im\nfolgenden werden einige dieser Analogien beleuchtet und regen hoffentlich zum Denken an. Ähnliches schon erlebt? Gibt es\nweitere Analogien? Stimmt gar nicht!",[18,3194,3195],{},"Falls nicht bekannt sein sollte, was wir im Berufsalltag so tun, helfen die Analogien vielleicht einen Eindruck zu\nbekommen.",[523,3197,3199],{"id":3198},"wenns-nicht-läuft-ist-immer-der-trainer-zuerst-am-pranger","Wenns nicht läuft ist immer der Trainer zuerst am Pranger",[18,3201,3202],{},"Der “Trainer” als Alibi Verantwortlicher. Es ist immer einfacher eine einzelne Person los zu werden, als das komplette\nTeam im Hintergrund.",[18,3204,3205],{},"In Firmen und Unternehmen geht es oftmals ähnlich von statten: Führungskräfte und Projektleiter werden ausgetauscht,\nwenn das Projektziel gefährdet ist. Unter neuer Führung geht es in die entgegengesetzte Richtung weiter, in der Hoffnung\nnun alles besser zu machen. Die bereits vorhandenen Probleme lassen sich auf diese Weise aber nicht zwangsläufig\nbeseitigen. Probleme können auch im Team, als auch im Umfeld verankert sein.",[523,3207,3209],{"id":3208},"ballbesitz-9010-und-keine-tore","Ballbesitz 90:10 und keine Tore",[18,3211,3212],{},"Kurzpassspiel, optimale Raumausnutzung, keine Fehlpässe. Für Taktik Liebhaber und Trainer sicherlich ein Leckerbissen.\nDoch nach 90 Minuten gewinnt die Mannschaft mit den meist erzielten Toren. Bei aller Leidenschaft und Liebe gewinnt am\nEnde das Ergebnis.",[18,3214,3215],{},"Auf ähnlich Art und Weise vertieft man sich auch gerne mal in ein Code Refactoring. Es soll ja keiner kommen und sagen\ndass hier könnte gegebenenfalls unter gewissen Umständen manchmal zu einem Bug oder einem Missverständnis führen. Es\nwerden so lange Codefragmente in andere Komponenten verschoben bis der eigene Zufriedensheitgrad erreicht wird. Am Ende\ndes Tages zählt jedoch der Mehrwert seitens des Anwenders.",[523,3217,3219],{"id":3218},"guardiola-vs-ancelotti-taktische-vorgaben-vs-laissez-faire","Guardiola vs Ancelotti (taktische Vorgaben vs Laissez-faire)",[18,3221,3222],{},"Guardiola und Ancelotti zählen aktuell beide zu den erfolgreichsten Trainern der Welt. Ihre Herangehensweisen und\nFührungsstile könnten jedoch kaum unterschiedlicher sein. Während Guardiola für millimetergenaue Laufwege und exakte\ntaktische Vorgaben bekannt ist, pflegt und befürwortet Ancelotti eher den Laissez-faire Stil.",[18,3224,3225],{},"In der Softwareentwicklung erinnert das z. B. an Besprechungen und deren Ablauf. Wir haben für das Refinement 60 Minuten\nangesetzt. Also sollten auch alle Themen innerhalb dieser Zeit besprochen sein. Zur Not fallen niedrig priorisierte\nThemen runter. Man hat schließlich noch andere Themen denen man sich zuwenden muss!",[18,3227,3228],{},"Oder vielleicht doch mal vom Fahrplan abweichen und das Refinement spontan verlängern, da gegen Ende eine interessante\nDiskussion entstanden ist?",[523,3230,3232],{"id":3231},"beidfüßig-und-alleskönner-oder-doch-einen-arjen-robben","Beidfüßig und Alleskönner oder doch einen Arjen Robben?",[18,3234,3235],{},"Arjen Robben ist im Fußball recht einzigartig. Er spielt offensiv auf der rechten Außenbahn, macht immer die gleiche\nFinte, ist linksfuß und hat den rechten Fuß nur, damit er beim laufen nicht umfällt. Auf anderen Positionen ist er lange\nnicht so effektiv. Dennoch ist er so wichtig für das Bayern Spiel, weil er Effizienz und das entscheidende Momentum mit\nbringt.",[18,3237,3238],{},"In der Softwareentwicklung kann man Robben das T-Modell gegenüber stellen. Dieses beschreibt spezialisiertes\nFachwissen (vertikaler Strich) und allgemeines Grundlagenwissen (horizontaler Strich). Es gibt in einem Team z. B.\njemanden der vorwiegend Interesse am Betrieb hat und jemanden der seinen Schwerpunkt in der Frontend Entwicklung hat.\nBeide können bei Abwesenheit des anderen aber auch dessen Arbeit erledigen. So hat man im Normalfall immer den Experten\ngriffbereit und der Experte darf auch mal Urlaub machen, ohne dass alles in seinem Bereich stehen bleibt.",[523,3240,3242],{"id":3241},"freistoß-spray-torlinientechnik-videobeweis","Freistoß-Spray, Torlinientechnik, Videobeweis, …",[18,3244,3245],{},"Das anfangs belächelte Freistoß-Spray verhindert das nachträgliche Verschieben des Balles. Die anfangs umstrittene\nTorlinientechnik verhindert Millionenverluste wegen menschlichen Fehlentscheidungen. Der immer noch umstrittene\nVideobeweis wird spielentscheidende Fehlentscheidungen zumindest dezimieren.",[18,3247,3248,3249,1250],{},"In der Softwareentwicklung müssen wir genauso am Ball bleiben (sind wir nich alle ein bisschen Fußball? 🙂 ). Themen wie\nz. B. Betrieb mit Containern dürfen natürlich kritisch hinterfragt werden. Virtuelle Maschinen tuns ja auch, richtig?\nOder komplizierte Frontend Build Tools. Zum erstellen eines Bundles reicht doch\n",[22,3250,3251],{},"cat awesomeStuff-001.js >> app.bundle.js",[18,3253,3254],{},"Insbesondere in unserer Branche gilt es lernbeständig zu sein, stetig neue Methoden und Technologien zu evaluieren und\nauch mutig zu sein diese schrittweise zu etablieren.",[523,3256,3258],{"id":3257},"barcelona-ist-kein-barcelona-wenn-messi-nicht-spielt","Barcelona ist kein Barcelona wenn Messi nicht spielt",[18,3260,3261],{},"Messi ist unbestritten einer der besten Fußballprofis des Planeten. Seine Aktionen und seine Anwesenheit bringt dem\nSpiel seines Teams den entscheidenden Vorteil. In Zahlen ausgedrückt hat die Argentinische Nationalmannschaft in der\nQualifikation zu Weltmeisterschaft 2018 mit Messi 6 Spiele bestritten und 5 gewonnen, ohne Messi 7 Spiele bestritten und\nnur 1 gewonnen. Das entspricht einer Siegquote von 83% (mit Messi) zu 14% (ohne Messi).",[18,3263,3264],{},"Dieses Thema lässt sich auf Teamarbeit im allgemeinen abbilden. Bezieht man sich auf den Ausfall eines Teammitglieds\nspricht man gerne vom sogenannten Truck Faktor. Wie viele Leute können vom Truck ausgeknockt werden, ohne den\nProjekterfolg zu gefährden. Finde eigentlich nur ich das etwas makaber?!? Naja, auf jeden fall darf der Erfolg nicht von\neiner Person abhängen. Bei uns gilt z. B. die Regel: kein Projekt wird mehr alleine bestritten. So steht einem drei\nWochenurlaub im Normalfall nichts entgegen, da die Last auf die Kollegen verteilt werden kann.",[523,3266,3268],{"id":3267},"wieso-wechselt-der-denn-jetzt-den-besten-spieler-aus","“Wieso wechselt der denn jetzt den besten Spieler aus?!”",[18,3270,3271],{},"Steht man im Stadion kann man nicht immer alle Trainerentscheidungen nachvollziehen. “Wieso spielt der mit dreier\nKette?!”, “Wieso fängt der ausgerechnet mit dieser Startelf an?!”, “Wieso wechselt der den denn jetzt aus?!”. Nach dem\nSpiel stellt sich dann vielleicht heraus, dass jener bester Spieler eine leichte Verletzung hatte.",[18,3273,3274],{},[1030,3275,3276],{},"Perspektivenwechsel / Empathie",[18,3278,3279],{},"Oft ist es hilfreich mal kurz durchzuatmen, einen Schritt zurück zu gehen und zu beobachten. Bei der Anforderungsanalyse\nz. B. hat es sich bewährt sogenannte Personas zu erstellen. Eine Persona ist die Beschreibung eines fiktiven Charakters\ndem die Software das Leben vereinfachen soll. Anhand der Persona kann das Team sich z. B. auf dessen Bedürfnisse\nfokussieren, statt mit noch mehr Vermutungen auch andere Möglichkeiten abzudecken.",[3152,3281,3282],{},[18,3283,3284],{},"“Von Sportlern lernen heißt siegen lernen”wird zu“Von Profis lernen heißt siegen lernen”",[18,3286,3287],{},"Profis im Sport haben alle etwas gemeinsames. Das gewisse Etwas weshalb sie erfolgreich wurden und weiterhin bleiben.\nSei es im Teamsport oder auch im Einzelsport (Tennis, Leichtathletik, …). Wir können viel von Ihnen lernen, bestimmt.\nAber auch Sportler können von uns lernen. Professionellen Softwareentwicklern, Agile Coaches und generell Teams im\nBerufsleben. In beide Richtungen lassen sich Analogien bilden. Probiert es aus. Experimentiert.",[18,3289,3290],{},"Und bleibt am Ball 😉",{"title":47,"searchDepth":69,"depth":69,"links":3292},[3293,3294,3295,3296,3297,3298,3299],{"id":3198,"depth":69,"text":3199},{"id":3208,"depth":69,"text":3209},{"id":3218,"depth":69,"text":3219},{"id":3231,"depth":69,"text":3232},{"id":3241,"depth":69,"text":3242},{"id":3257,"depth":69,"text":3258},{"id":3267,"depth":69,"text":3268},[811],"2017-05-04T12:33:31","https://synyx.de/blog/von-profis-lernen-heisst-siegen-lernen/",{},"/blog/von-profis-lernen-heisst-siegen-lernen",{"title":3143,"description":47},"von-profis-lernen-heisst-siegen-lernen","blog/von-profis-lernen-heisst-siegen-lernen",[3309,1570,3310],"analogien","profisport","Der nächste Sprint steht an… Wir treffen uns am Dienstag Morgen um 10:00 Uhr zum Refinement. Es gilt noch einige Themen fachlich genauer zu hinterfragen. Auch technische Grundlagen müssen noch…","vic-ozc1OThUJSebUDBMCTXgtICWalJTj6v7retM5z0",{"id":3314,"title":3315,"author":3316,"body":3317,"category":3719,"date":3720,"description":3324,"extension":815,"link":3721,"meta":3722,"navigation":303,"path":3723,"seo":3724,"slug":3321,"stem":3725,"tags":3726,"teaser":3730,"__hash__":3731},"blog/blog/bean-x-of-type-y-is-not-eligible-for-getting-processed-by-all-beanpostprocessors.md","Bean X of type Y is not eligible for getting processed by all BeanPostProcessors",[2945],{"type":11,"value":3318,"toc":3711},[3319,3322,3325,3329,3332,3335,3377,3397,3402,3411,3417,3433,3437,3444,3468,3475,3479,3482,3494,3498,3501,3512,3519,3522,3528,3542,3570,3573,3577,3580,3608,3611,3662,3683,3692,3695,3699,3702,3705,3708],[14,3320,3315],{"id":3321},"bean-x-of-type-y-is-not-eligible-for-getting-processed-by-all-beanpostprocessors",[18,3323,3324],{},"Recently we had a problem related Springs auto-proxy feature that I think is worth writing about.",[523,3326,3328],{"id":3327},"the-problem","The Problem",[18,3330,3331],{},"We use Spring as our framework of choice because it provides us with a nice set of convenience features when\nbootstrapping and plugging together our application.",[18,3333,3334],{},"One of these features is caching: We cache our users’ roles because their definitions are stored in a pretty slow\nexternal system and change rarely.",[42,3336,3338],{"className":1403,"code":3337,"language":1405,"meta":47,"style":47},"\n@Component\npublic class RoleRepository {\n @Cacheable(CacheConfig.ROLES_NAME)\n public Set\u003CRole> loadRoles() {\n // .. slow call to external system\n }\n}\n\n",[22,3339,3340,3344,3349,3354,3359,3364,3369,3373],{"__ignoreMap":47},[51,3341,3342],{"class":53,"line":54},[51,3343,304],{"emptyLinePlaceholder":303},[51,3345,3346],{"class":53,"line":69},[51,3347,3348],{},"@Component\n",[51,3350,3351],{"class":53,"line":82},[51,3352,3353],{},"public class RoleRepository {\n",[51,3355,3356],{"class":53,"line":205},[51,3357,3358],{}," @Cacheable(CacheConfig.ROLES_NAME)\n",[51,3360,3361],{"class":53,"line":241},[51,3362,3363],{}," public Set\u003CRole> loadRoles() {\n",[51,3365,3366],{"class":53,"line":250},[51,3367,3368],{}," // .. slow call to external system\n",[51,3370,3371],{"class":53,"line":256},[51,3372,1338],{},[51,3374,3375],{"class":53,"line":262},[51,3376,253],{},[18,3378,3379,3380,3383,3384,3387,3388,3393,3394,3396],{},"Since our ",[22,3381,3382],{},"RoleRepository"," is a component managed by Spring it gets picked up automatically during boot and\n",[22,3385,3386],{},"loadRoles()"," gets backed by a cache. Spring implements this by proxying our repository.\nSee ",[540,3389,3392],{"href":3390,"rel":3391},"http://docs.spring.io/spring/docs/current/spring-framework-reference/html/cache.html",[544],"Spring Reference Documentation","\nfor details. As a result the “real” ",[22,3395,3386],{}," method gets triggered only once in 10 minutes and all the other calls\nare served from the cache.",[18,3398,3399,3400,1250],{},"After several sprints we noticed a problem with the caching. While in caching worked for other beans it stopped working\nfor the shown ",[22,3401,3382],{},[18,3403,3404,3405,3407,3408,3410],{},"We noticed this because our health-check which (indirectly) triggers the ",[22,3406,3386],{}," and runs every 5 seconds did not\nhit the cache and therefore produced a log-entry every 5 seconds. The cache for roles was empty, regardless of how\noften ",[22,3409,3386],{}," was called.",[18,3412,3413,3414,3416],{},"While debugging the issue we found out that the proxy that should do the caching was not generated for ",[22,3415,3382],{},".\nDuring bootstrap of the application Spring gave us a corresponding hint:",[42,3418,3422],{"className":3419,"code":3420,"language":3421,"meta":47,"style":47},"language-plaintext shiki shiki-themes github-light github-dark","\nINFO 7189 --- [ main] trationDelegate$BeanPostProcessorChecker : Bean 'RoleRepository' of type [class ...RoleRepository] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)\n\n","plaintext",[22,3423,3424,3428],{"__ignoreMap":47},[51,3425,3426],{"class":53,"line":54},[51,3427,304],{"emptyLinePlaceholder":303},[51,3429,3430],{"class":53,"line":69},[51,3431,3432],{},"INFO 7189 --- [ main] trationDelegate$BeanPostProcessorChecker : Bean 'RoleRepository' of type [class ...RoleRepository] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)\n",[523,3434,3436],{"id":3435},"what-is-a-beanpostprocessor","What is a BeanPostProcessor",[18,3438,3439,3440,3443],{},"A ",[22,3441,3442],{},"BeanPostProcessor"," is a special component that allows to manipulate other beans after they are created.",[18,3445,3446,3447,3450,3451,3454,3455,146,3458,146,3461,3464,3465,3467],{},"These postprocessors can manipulate beans or even replace them completely. Spring ships with several implementations\nthat do all kinds of stuff. For example there is one that checks if a bean implements ",[22,3448,3449],{},"ApplicationContextAware"," and sets\nthe ",[22,3452,3453],{},"ApplicationContext"," if so. Also many of the “proxy-stuff” for ",[22,3456,3457],{},"@Async",[22,3459,3460],{},"@Transactional",[22,3462,3463],{},"@Caching"," and so on is\ndone using a ",[22,3466,3442],{},". You can also implement your own postprocessors.",[18,3469,3470,3471,3474],{},"To be able to postprocess all beans with all ",[22,3472,3473],{},"BeanPostProcessors"," Spring has to create them before it creates the\n“regular” beans.",[523,3476,3478],{"id":3477},"the-chicken-and-the-egg","The Chicken and the Egg",[18,3480,3481],{},"But you can also inject Spring beans into postprocessors. If you inject a bean into a postprocessor Spring has to create\nthis bean even before it creates the corresponding postprocessor you inject it to. In this case it no longer guarantees\nthat all postprocessors are able to process the injected bean but instead logs the message shown above.",[18,3483,3484,3485,3487,3488,3491,3492,1250],{},"In our example the log-message means that there is one postprocessor that (directly or indirectly) leads to creation of\nour ",[22,3486,3382],{}," and there are more postprocessors to be created later (probaly the one that handles ",[22,3489,3490],{},"@Cachable",")\nthat will not be able to post-process our ",[22,3493,3382],{},[523,3495,3497],{"id":3496},"debugging-the-problem","Debugging the Problem",[18,3499,3500],{},"Unfortunately the log-entry does not help very much in finding out what exactly is the problem.",[18,3502,3503,3504,3507,3508,3511],{},"The statement is produced by ",[22,3505,3506],{},"BeanPostProcessorChecker"," which is an inner class in ",[22,3509,3510],{},"PostProcessorRegistrationDelegate",".\nTo find out what exactly happens here I set a breakpoint right at the log-statement and booted our application in\ndebug-mode. Since there are several beans created I waited for the statement in question (in our case the one with\nbeanName = roleRepository).",[18,3513,3514,3515,3518],{},"To find out what caused the creation of our roleRepository I simply followed the call-stack down the ",[22,3516,3517],{},"getObject","\nmethods. In our case the chain was:",[18,3520,3521],{},"roleRepository -> authorityService -> customerPermissionEvaluator -> delegatingPermissionEvaluator ->\ncustomMethodSecurityConfiguration -> methodSecurityMetadataSource -> (… more Spring setup code …)",[18,3523,3524],{},[960,3525],{"alt":3526,"src":3527},"\"debugging_getobject\"","https://media.synyx.de/uploads//2016/11/debugging_getObject.png",[18,3529,3530,3531,3533,3534,3537,3538,3541],{},"So the ",[22,3532,3382],{}," is created because it is needed by our custom implementation of Spring Securities\n",[22,3535,3536],{},"PermissionEvaluator","-Interface that is used to evaluate security related expressions like the ones that can be used\nwith ",[22,3539,3540],{},"@PreAuthorize",":",[42,3543,3545],{"className":1403,"code":3544,"language":1405,"meta":47,"style":47},"\n@PreAuthorize(\"hasPermission(#customer, AUTHORITY_BILLING)\")\npublic Optional\u003CBillingDoc> findById(Customer customer, String documentId) {\n // boring business logic here\n}\n\n",[22,3546,3547,3551,3556,3561,3566],{"__ignoreMap":47},[51,3548,3549],{"class":53,"line":54},[51,3550,304],{"emptyLinePlaceholder":303},[51,3552,3553],{"class":53,"line":69},[51,3554,3555],{},"@PreAuthorize(\"hasPermission(#customer, AUTHORITY_BILLING)\")\n",[51,3557,3558],{"class":53,"line":82},[51,3559,3560],{},"public Optional\u003CBillingDoc> findById(Customer customer, String documentId) {\n",[51,3562,3563],{"class":53,"line":205},[51,3564,3565],{}," // boring business logic here\n",[51,3567,3568],{"class":53,"line":241},[51,3569,253],{},[18,3571,3572],{},"So when Spring Security is bootstrapped (which seems to be done before caching is) it also initializes a part of our\n“business-beans” which then cannot be postprocessed with caching.",[523,3574,3576],{"id":3575},"the-fix","The Fix",[18,3578,3579],{},"To fix the problem I cut the eager dependency from our security-code and replaced it by a lazy one.",[42,3581,3583],{"className":1403,"code":3582,"language":1405,"meta":47,"style":47},"\n@Autowired\npublic CustomerPermissionEvaluator(AuthorityService authorityService) {\n this.authorityService = authorityService;\n}\n\n",[22,3584,3585,3589,3594,3599,3604],{"__ignoreMap":47},[51,3586,3587],{"class":53,"line":54},[51,3588,304],{"emptyLinePlaceholder":303},[51,3590,3591],{"class":53,"line":69},[51,3592,3593],{},"@Autowired\n",[51,3595,3596],{"class":53,"line":82},[51,3597,3598],{},"public CustomerPermissionEvaluator(AuthorityService authorityService) {\n",[51,3600,3601],{"class":53,"line":205},[51,3602,3603],{}," this.authorityService = authorityService;\n",[51,3605,3606],{"class":53,"line":241},[51,3607,253],{},[18,3609,3610],{},"was changed to",[42,3612,3614],{"className":1403,"code":3613,"language":1405,"meta":47,"style":47},"\n@Autowired\npublic CustomerPermissionEvaluator(ObjectFactory\u003CAuthorityService> authorityServiceObjectFactory) {\n this.authorityServiceObjectFactory = authorityServiceObjectFactory;\n}\n@Override\npublic boolean hasPermission(Authentication authentication, Object targetDomainObject, Object permission) {\n AuthorityService service = this.authorityServiceObjectFactory.getObject();\n // ... do stuff with service\n}\n\n",[22,3615,3616,3620,3624,3629,3634,3638,3643,3648,3653,3658],{"__ignoreMap":47},[51,3617,3618],{"class":53,"line":54},[51,3619,304],{"emptyLinePlaceholder":303},[51,3621,3622],{"class":53,"line":69},[51,3623,3593],{},[51,3625,3626],{"class":53,"line":82},[51,3627,3628],{},"public CustomerPermissionEvaluator(ObjectFactory\u003CAuthorityService> authorityServiceObjectFactory) {\n",[51,3630,3631],{"class":53,"line":205},[51,3632,3633],{}," this.authorityServiceObjectFactory = authorityServiceObjectFactory;\n",[51,3635,3636],{"class":53,"line":241},[51,3637,253],{},[51,3639,3640],{"class":53,"line":250},[51,3641,3642],{},"@Override\n",[51,3644,3645],{"class":53,"line":256},[51,3646,3647],{},"public boolean hasPermission(Authentication authentication, Object targetDomainObject, Object permission) {\n",[51,3649,3650],{"class":53,"line":262},[51,3651,3652],{}," AuthorityService service = this.authorityServiceObjectFactory.getObject();\n",[51,3654,3655],{"class":53,"line":272},[51,3656,3657],{}," // ... do stuff with service\n",[51,3659,3660],{"class":53,"line":282},[51,3661,253],{},[18,3663,3664,3665,3668,3669,3672,3673,3676,3677,3680,3681,913],{},"By using Springs ",[22,3666,3667],{},"ObjectFactory"," and calling its ",[22,3670,3671],{},"getObject()"," later (not during construction of the\n",[22,3674,3675],{},"CustomerPermissionEvaluator",") we delay the creation of ",[22,3678,3679],{},"AuthorityService"," (and of beans it needs such as the\n",[22,3682,3382],{},[18,3684,3685,3686,3688,3689,1250],{},"By doing so they can later be processed by all ",[22,3687,3473],{}," and the log message in question disappeares &\ncaching works again ",[1030,3690,3691],{},"o/",[18,3693,3694],{},"Note that there are other ways to solve the problem. One other thing I can think of is to change the order the\nPostProcessors are created.",[523,3696,3698],{"id":3697},"final-thougths","Final Thougths",[18,3700,3701],{},"In conclusion you might want to actively watch the statements Spring logs at INFO and above during your applications\nbootstrap.",[18,3703,3704],{},"Especially the mentioned statement about beans being not eligible for auto-proxying should not contain any of your\nnon-infrastructure beans.",[18,3706,3707],{},"Also make sure there is no eager dependency between your infrastructure-related code and your business-logic. At least\ndouble-check and challenge these decisions. This is especially true because dependency-graphs get pretty big pretty\nfast. In our example there were 20 other “business” beans not eligable for auto-proxying because of the same eager\ndependency.",[799,3709,3710],{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":47,"searchDepth":69,"depth":69,"links":3712},[3713,3714,3715,3716,3717,3718],{"id":3327,"depth":69,"text":3328},{"id":3435,"depth":69,"text":3436},{"id":3477,"depth":69,"text":3478},{"id":3496,"depth":69,"text":3497},{"id":3575,"depth":69,"text":3576},{"id":3697,"depth":69,"text":3698},[810],"2016-11-04T09:45:17","https://synyx.de/blog/bean-x-of-type-y-is-not-eligible-for-getting-processed-by-all-beanpostprocessors/",{},"/blog/bean-x-of-type-y-is-not-eligible-for-getting-processed-by-all-beanpostprocessors",{"title":3315,"description":3324},"blog/bean-x-of-type-y-is-not-eligible-for-getting-processed-by-all-beanpostprocessors",[3727,1570,1405,3728,3729],"bug","proxy","spring","Recently we had a problem related Springs auto-proxy feature that I think is worth writing about. The Problem We use Spring as our framework of choice because it provides us…","g_18Z38wUIExfcwJbi-RkuL033DdE2J3_WTwAuhdGNo",{"id":3733,"title":3734,"author":3735,"body":3737,"category":3987,"date":3988,"description":3989,"extension":815,"link":3990,"meta":3991,"navigation":303,"path":3992,"seo":3993,"slug":3741,"stem":3995,"tags":3996,"teaser":4000,"__hash__":4001},"blog/blog/entwicklertag-frankfurt-2015.md","Entwicklertag Frankfurt 2015",[1692,3736],"schneider",{"type":11,"value":3738,"toc":3980},[3739,3742,3787,3791,3805,3811,3815,3823,3832,3847,3856,3860,3868,3874,3877,3880,3886,3895,3898,3902,3944,3950,3954,3957,3966,3975],[14,3740,3734],{"id":3741},"entwicklertag-frankfurt-2015",[18,3743,3744,3745,3750,3751,3756,3757,3762,3763,3768,3769,3774,3775,3780,3781,3786],{},"Während der ",[540,3746,3749],{"href":3747,"rel":3748},"http://www.entwicklertag.de/",[544],"Karlsruher Entwicklertag"," der ",[540,3752,3755],{"href":3753,"rel":3754},"http://www.andrena.de/",[544],"andrena objects ag","\nschon seit 2010 ein etabliertes Event in Karlsruhe ist, brachte der Veranstalter die Konferenz dieses Jahr erst zum\nzweiten Mal nach Frankfurt. Fünf synyx-Kollegen dachten sich: “Die nehmen wir doch mit!” und bestiegen den ICE in\nRichtung Deutschlands Bankenmetropole. Die Entwicklergemeinde wurde dort in einem Gebäude der Goethe-Universität\nempfangen und mit ausreichend Kaffee und Mate für den ganzen Tag versorgt. Bereits in der Opening Session zeichnete sich\ndie hohe Qualität der Veranstaltung ab als ",[540,3758,3761],{"href":3759,"rel":3760},"http://sdq.ipd.kit.edu/people/ralf_reussner/",[544],"Professor Ralf Reussner","\nvom ",[540,3764,3767],{"href":3765,"rel":3766},"http://www.kit.edu/index.php",[544],"KIT"," einen\neindrucksvollen ",[540,3770,3773],{"href":3771,"rel":3772},"https://entwicklertag.de/frankfurt/2015/opening-session-was-brauchen-softwaretechniker-um-ingenieure-zu-werden",[544],"Einblick in die Informatik-Forschung","\nbot. Mit den von ihm betreuten Forschungsprojekten ",[540,3776,3779],{"href":3777,"rel":3778},"http://www.palladio-simulator.com/",[544],"Palladio","\nund ",[540,3782,3785],{"href":3783,"rel":3784},"https://sdqweb.ipd.kit.edu/wiki/Vitruvius",[544],"Vitruvius (WIP)"," stellte er mächtige Werkzeuge vor, mit denen man schon\nvor der Implementierung eines Projekts die Auswirkungen von Designentscheidungen abschätzen kann. Nach diesem gelungenen\nAuftakt begann die eigentliche Konferenz mit in drei Tracks strukturierten Talks.",[1503,3788,3790],{"id":3789},"leadership-hacks","Leadership Hacks",[18,3792,3793,3794,3799,3800,3804],{},"Eine der originellsten und interessantesten Sessions war ",[540,3795,3798],{"href":3796,"rel":3797},"https://twitter.com/benjamin",[544],"Benjamin Reitzammers"," Talk\nüber ",[540,3801,3790],{"href":3802,"rel":3803},"https://entwicklertag.de/frankfurt/2015/5-leadership-hacks-oder-wie-ich-meine-ideen-umgesetzt-bekomme",[544],".\nStatt der erwarteten Tricks zur Mitarbeitermanipulation lieferte er eine Anleitung, wie man durch sensibles und\nrücksichtsvolles Teamplayerverhalten seine Kollegen auf die eigene Seite bringt. Dabei spielen so einfache Dinge wie\nMittagessen und Zuhören schon eine große Rolle. Er appellierte in diesem Zusammenhang an Eigenschaften wie Sensibilität,\nVerlässlichkeit, Selbstreflexion und Kommunikation. Seine wichtigste Message: Don’t be a Rockstar!",[18,3806,3807],{},[960,3808],{"alt":3809,"src":3810},"\"rockstar\"","https://media.synyx.de/uploads//2015/02/rockstar.png",[1503,3812,3814],{"id":3813},"pecha-kucha","Pecha Kucha",[18,3816,3817,3818,3822],{},"Eine für uns neue Vortragstechnik, welche wir auf den Frankfurter Entwicklertagen kennenlernen durften, war\ndas ",[540,3819,3814],{"href":3820,"rel":3821},"http://de.wikipedia.org/wiki/Pecha_Kucha",[544],". Bei dieser Form des Vortrages wird die Anzahl der Folien\nauf 20 sowie die Projektionszeit je Folie auf 20 Sekunden begrenzt. Das Format versprach viel Informationen in kurzer\nZeit und so war es auch. Zudem gab es viel zu lachen. Ein durchaus gelungenes Format mit tollen Vorträgen.",[18,3824,3825,3826,3831],{},"Dr. Michael Eichberg gelang es in seinem\nVortrag ",[540,3827,3830],{"href":3828,"rel":3829},"http://www.entwicklertag.de/frankfurt/2015/your-jdk-devil-dark",[544],"Your JDK – The Devil is in the Dark"," mit kurzen\nund durchaus amüsanten Codeausschnitten des OpenJDK 8 einen Einblick in die kleinen Abgründe der Programmierung zu\nvermitteln. Unser Vorschlag für den Leadership Hack #6:Your JDK – The Devil is in the Dark mit einem Kollegen\nanschauen.",[18,3833,3834,3835,3840,3841,3846],{},"Im Votrag ",[540,3836,3839],{"href":3837,"rel":3838},"http://www.entwicklertag.de/frankfurt/2015/its-all-about-fun",[544],"It’s All About The Fun"," von Jens Schauder gab\nes einen kurzen Abriss über die Wichtigkeit des Spaßes in einem Unternehmen und dass dieser nie zu kurz kommen darf.\nAuch durchaus ungeliebte Aufgaben können mit der ",[540,3842,3845],{"href":3843,"rel":3844},"http://de.wikipedia.org/wiki/Pomodoro-Technik",[544],"Pomodoro-Technik"," und\neinem ‘Bonbon’ nach dieser Tätigkeit zu einem positiven Effekt führen. Ein schöner Vortrag dem wir uns nur anschließen\nkönnen. Spaß bei der Arbeit zeigt uns, warum wir uns dazu entschlossen haben, unser Hobby zum Beruf zu machen.",[18,3848,3849,3850,3855],{},"Jeder hat ihn in der Firma und kennt ihn nur zu gut.\nDen ",[540,3851,3854],{"href":3852,"rel":3853},"http://www.entwicklertag.de/frankfurt/2015/der-prozess-sheriff-eine-unliebsame-spezies",[544],"Prozess Sheriff",". Er\nbeharrt darauf den Prozess strikt und ohne Kompromisse zu folgen. Ob dieser in einem agilen Umfeld mit Scrum noch eine\nBerechtigung hat stellte uns Steffen Brandt vor. Ein netter Talk um sich an seine Kollegen zu erinnern und ein wenig zu\nschmunzeln.",[1503,3857,3859],{"id":3858},"antifragile-software-für-die-welt-des-21-jahrhunderts","Antifragile Software für die Welt des 21. Jahrhunderts",[18,3861,3862,3863,3867],{},"Der Nachmittag startete mit einem sehr spannenden Vortrag von Johannes\nLink, ",[540,3864,3859],{"href":3865,"rel":3866},"https://entwicklertag.de/frankfurt/2015/keynote-antifragile-software-f%C3%BCr-die-welt-des-21-jahrhunderts",[544],",\nüber die Antifragilität von Systemen und was dies bedeutet. Fragil ist wahrscheinlich jedem von uns ein Begriff. Es\nbeschreibt die Eigenschaft eines Systems, welches durch starke Belastungen von außen beschädigt oder sogar zerstört\nwerden könnte. Entscheidend dabei ist, dass dieses Verhalten deterministisch anhand der Stärke der Belastung wiederholt\nhervorgerufen werden kann.",[18,3869,3870],{},[960,3871],{"alt":3872,"src":3873},"\"fragil\"","https://media.synyx.de/uploads//2015/02/fragil.png",[18,3875,3876],{},"Natürliche Systeme wie zum Beispiel die Menschheit sind hingegen antifragil. Sie werden durch Belastungen ihrer Umwelt\nzunächst stärker, bis die Belastung zu extrem wird und eine negative Wirkung eintritt.",[18,3878,3879],{},"Wenn man dieses Konzept auf Softwaresysteme anwenden könnte, würde dies bedeuten, dass ein System sich selbst\nstabilisiert und anhand von, zum Beispiel, unüblichen Anfragen lernen würde. Das System würde robuster werden. Dieser\nAnsatz ist neu und sehr spannend und wirft die Frage auf, ob man ein System erschaffen kann, welches z.B. anhand von\nzyklisch wiederkehrenden selbst initiierten DoS Attacken antifragiler werden würde, sozusagen eine DoS Impfung erhält?",[18,3881,3882],{},[960,3883],{"alt":3884,"src":3885},"\"hormesis\"","https://media.synyx.de/uploads//2015/02/hormesis2.png",[18,3887,3888,3889,3894],{},"In der Natur wird dieses Phänomen ",[540,3890,3893],{"href":3891,"rel":3892},"http://de.wikipedia.org/wiki/Hormesis",[544],"Hormesis"," genannt. Bei der ein Organismus,\nbzw. in unserem Fall ein System, eine positive Wirkung bei Belastung erfährt. Erst bei einer Überdosis ist die\nFunktionalität nicht mehr gewährleistet und das System kann seinen Dienst nicht mehr erfüllen.",[18,3896,3897],{},"Zusammengefasst ein sehr guter Vortrag und ein spannendes Themengebiet. Mehr davon!",[1503,3899,3901],{"id":3900},"weitere-vorträge","Weitere Vorträge",[18,3903,3904,3905,3908,3909,3914,3915,3780,3920,3925,3926,3931,3932,3937,3938,3943],{},"Auch von den restlichen Vorträgen waren einige erwähnenswert. Hagen Buchwald vom\nVeranstalter ",[540,3906,3755],{"href":3753,"rel":3907},[544]," erläuterte auf hohem Niveau und mit vielen Erfahrungen im\nGepäck inwiefern der Pfad eines Unternehmens zur Agilität von\nder ",[540,3910,3913],{"href":3911,"rel":3912},"https://entwicklertag.de/frankfurt/2015/agile-transition-core-culture-matters",[544],"Unternehmenskultur"," abhängt. Den\ngoldenen Pixel für den kreativsten Vortrag bekommen ",[540,3916,3919],{"href":3917,"rel":3918},"https://twitter.com/jensbroos",[544],"Jens Broos",[540,3921,3924],{"href":3922,"rel":3923},"https://twitter.com/mrupilo",[544],"Martin Ruprecht"," verliehen, die durch eine beeindruckende Anzahl von Analogien\nbelegten, was\ndie ",[540,3927,3930],{"href":3928,"rel":3929},"https://entwicklertag.de/frankfurt/2015/was-wir-software-entwickler-vom-modernen-fussball-lernen-k%C3%B6nnen",[544],"Softwareentwicklung mit Fußball","\ngemein hat und was wir uns bei unserer täglichen Arbeit von den Kickern abschauen sollten. Einer der wenigen Vorträge\nmit Code auf den Folien war der Talk von ",[540,3933,3936],{"href":3934,"rel":3935},"https://twitter.com/dasniko",[544],"Niko Köbler",", der verschiedene Wege und\nToolchains\naufzeigte, ",[540,3939,3942],{"href":3940,"rel":3941},"https://entwicklertag.de/frankfurt/2015/nodejs-auf-der-jvm-nodyn-und-avatarjs-im-vergleich",[544],"Node.js auf der JVM","\nzu nutzen, wobei anscheinend Nashörner aller Art eine größere Rolle spielen.",[18,3945,3946],{},[960,3947],{"alt":3948,"src":3949},"\"aufstellung\"","https://media.synyx.de/uploads//2015/02/aufstellung.png",[1503,3951,3953],{"id":3952},"fazit","Fazit",[18,3955,3956],{},"Wieder zurück auf unserem heimischen Bürostuhl können wir sagen, dass der Entwicklertag Frankfurt eine nette kleine\nKonferenz ist, die man sich anschauen sollte, wenn man in der Nähe ist.",[18,3958,3959,3960,3965],{},"Die Qualität der Talks war hoch, die Atmosphäre angenehm und das Catering ließ kaum Wünsche offen. Die Folien\nsind ",[540,3961,3964],{"href":3962,"rel":3963},"http://www.entwicklertag.de/frankfurt/2015/programm",[544],"hier"," öffentlich zugänglich. Das Einzige Manko dieses Jahr:\nEs wurden für unseren Geschmack zu wenige Informationen vermittelt, die unsere konkrete tägliche Arbeit als Entwickler\nverbessern können. Zu einem großen Anteil wurden die Themen in Form abstrakter Konzepte auf hohem Level behandelt.\nObwohl wir das generell begrüßen und die Themen dank der guten Speaker interessant präsentiert wurden, haben wir mehr\nkonkrete Beispiele und Vermittlung von Best Practices vermisst. Das Fazit der Konferenz ist daher: Neat stuff, but too\nmeta. Relevant XKCD:",[18,3967,3968],{},[540,3969,3972],{"href":3970,"rel":3971},"http://xkcd.com/1447/",[544],[960,3973],{"alt":47,"src":3974},"http://imgs.xkcd.com/comics/meta-analysis.png",[18,3976,3977],{},[910,3978,3979],{},"Quelle: xkcd.com",{"title":47,"searchDepth":69,"depth":69,"links":3981},[3982,3983,3984,3985,3986],{"id":3789,"depth":82,"text":3790},{"id":3813,"depth":82,"text":3814},{"id":3858,"depth":82,"text":3859},{"id":3900,"depth":82,"text":3901},{"id":3952,"depth":82,"text":3953},[810],"2015-02-26T17:41:10","Während der Karlsruher Entwicklertag der andrena objects ag\\nschon seit 2010 ein etabliertes Event in Karlsruhe ist, brachte der Veranstalter die Konferenz dieses Jahr erst zum\\nzweiten Mal nach Frankfurt. Fünf synyx-Kollegen dachten sich: “Die nehmen wir doch mit!” und bestiegen den ICE in\\nRichtung Deutschlands Bankenmetropole. Die Entwicklergemeinde wurde dort in einem Gebäude der Goethe-Universität\\nempfangen und mit ausreichend Kaffee und Mate für den ganzen Tag versorgt. Bereits in der Opening Session zeichnete sich\\ndie hohe Qualität der Veranstaltung ab als Professor Ralf Reussner\\nvom KIT einen\\neindrucksvollen Einblick in die Informatik-Forschung\\nbot. Mit den von ihm betreuten Forschungsprojekten Palladio\\nund Vitruvius (WIP) stellte er mächtige Werkzeuge vor, mit denen man schon\\nvor der Implementierung eines Projekts die Auswirkungen von Designentscheidungen abschätzen kann. Nach diesem gelungenen\\nAuftakt begann die eigentliche Konferenz mit in drei Tracks strukturierten Talks.","https://synyx.de/blog/entwicklertag-frankfurt-2015/",{},"/blog/entwicklertag-frankfurt-2015",{"title":3734,"description":3994},"Während der Karlsruher Entwicklertag der andrena objects ag\nschon seit 2010 ein etabliertes Event in Karlsruhe ist, brachte der Veranstalter die Konferenz dieses Jahr erst zum\nzweiten Mal nach Frankfurt. Fünf synyx-Kollegen dachten sich: “Die nehmen wir doch mit!” und bestiegen den ICE in\nRichtung Deutschlands Bankenmetropole. Die Entwicklergemeinde wurde dort in einem Gebäude der Goethe-Universität\nempfangen und mit ausreichend Kaffee und Mate für den ganzen Tag versorgt. Bereits in der Opening Session zeichnete sich\ndie hohe Qualität der Veranstaltung ab als Professor Ralf Reussner\nvom KIT einen\neindrucksvollen Einblick in die Informatik-Forschung\nbot. Mit den von ihm betreuten Forschungsprojekten Palladio\nund Vitruvius (WIP) stellte er mächtige Werkzeuge vor, mit denen man schon\nvor der Implementierung eines Projekts die Auswirkungen von Designentscheidungen abschätzen kann. Nach diesem gelungenen\nAuftakt begann die eigentliche Konferenz mit in drei Tracks strukturierten Talks.","blog/entwicklertag-frankfurt-2015",[1799,3997,1682,3998,1685,3999,828],"ausbildung","etffm","scrum","Während der Karlsruher Entwicklertag der andrena objects ag schon seit 2010 ein etabliertes Event in Karlsruhe ist, brachte der Veranstalter die Konferenz dieses Jahr erst zum zweiten Mal nach Frankfurt.…","Btw4IgxkifwFP3VIqPjGqsCL8uf60AJ4xKsGG3mj-_E",{"id":4003,"title":4004,"author":4005,"body":4007,"category":4257,"date":4259,"description":4260,"extension":815,"link":4261,"meta":4262,"navigation":303,"path":4263,"seo":4264,"slug":4011,"stem":4266,"tags":4267,"teaser":4270,"__hash__":4271},"blog/blog/javascript-linting-tool-evaluation.md","Javascript Linting Tool Evaluation",[4006,3736],"mueller",{"type":11,"value":4008,"toc":4248},[4009,4012,4026,4051,4060,4063,4067,4135,4138,4141,4154,4169,4172,4189,4192,4195,4206,4210,4213,4216,4219,4230,4234,4237,4243,4245],[14,4010,4004],{"id":4011},"javascript-linting-tool-evaluation",[18,4013,4014,4015,4020,4021,4025],{},"In our internal JavaScript ‘User Group’ (called JS-Posse in honour of the\nlegendary ‘",[540,4016,4019],{"href":4017,"rel":4018},"http://www.javaposse.com",[544],"The Java Posse","‘ by Dick Wall, Chet Haase et al.), we recently decided to evaluate\nalternatives to our current JavaScript linting standart, JSHint. Although well established by now among different\ndevelopment teams across ",[540,4022,829],{"href":4023,"rel":4024},"http://www.synyx.de",[544],", using it never felt 100% comfortable. A quick Google search left\nus with three alternatives:",[903,4027,4028,4036,4043],{},[906,4029,4030,4035],{},[540,4031,4034],{"href":4032,"rel":4033},"http://jslint.com",[544],"JSLint"," by Doug Crockford himself",[906,4037,4038],{},[540,4039,4042],{"href":4040,"rel":4041},"https://developers.google.com/closure/utilities/",[544],"Closure Linter by Google",[906,4044,4045,4050],{},[540,4046,4049],{"href":4047,"rel":4048},"http://eslint.org",[544],"ESLint",", the new kid on the block",[18,4052,4053,4054,4059],{},"…as well as ",[540,4055,4058],{"href":4056,"rel":4057},"http://jshint.com/",[544],"JSHint"," itself, of course.",[18,4061,4062],{},"We drew up a quick spreadsheet for evaluating the tools and came up with the following.",[523,4064,4066],{"id":4065},"criteria","Criteria",[903,4068,4069,4075,4081,4087,4093,4099,4105,4111,4117,4123,4129],{},[906,4070,4071,4074],{},[1030,4072,4073],{},"Performance"," How long does it take to run over our example project, a single page webapp with a couple of thousands\nof JavaScript LOC?",[906,4076,4077,4080],{},[1030,4078,4079],{},"Licensing"," Does the license meet our requirements (and those of our customers, of course)?",[906,4082,4083,4086],{},[1030,4084,4085],{},"Project health/adoption"," How healthy is the project? Is it on Github, and is it well maintained?",[906,4088,4089,4092],{},[1030,4090,4091],{},"Completeness of configurations"," Does the tool cover all our use-cases for a linting tool?",[906,4094,4095,4098],{},[1030,4096,4097],{},"Productivity (rule set creation / project setup)"," When creating a new project, is it difficult to create a matching\nruleset? Does the tool come with a reasonable default rule set, or do you need to set up all the checks yourself?",[906,4100,4101,4104],{},[1030,4102,4103],{},"Productivity (active software development)"," During active development, does the tool assist the developer in\nwriting quality code, or does it bully you to the point where you’d rather abolish using a linting tool at all?",[906,4106,4107,4110],{},[1030,4108,4109],{},"Quality of Documentation/Tutorials/Self Help"," How good is the project documentation? When the tool breaks the build\nwith a certain error message, how difficult is it to find reliable information on the error in question (why does it\noccur, why is it a bad practice, how to fix it)?",[906,4112,4113,4116],{},[1030,4114,4115],{},"Ability to integrate with existing projects"," Is it possible to integrate the linting tool in an existing projects\nwithout making changes to the project to comply to the rules?",[906,4118,4119,4122],{},[1030,4120,4121],{},"Integration with build tool"," Is it possible to integrate the linting tool into your build chain to receive direct\nfeedback?",[906,4124,4125,4128],{},[1030,4126,4127],{},"ES6 support"," How well does the project support future versions of the language?",[906,4130,4131,4134],{},[1030,4132,4133],{},"Pluggable"," Is it possible to extend the given rule set with custom checks?",[523,4136,4058],{"id":4137},"jshint",[18,4139,4140],{},"The first tool we looked at was the already-familiar JSHint. We already knew what was bothering us about it:",[903,4142,4143,4151],{},[906,4144,4145,4146,4150],{},"Its hard to find the documentation for a certain error message. While the error messages itself are mostly\nself-explanatory, it can be somewhat difficult to find out how to deactivate or customize a certain rule. For\nexample, ",[540,4147,4148],{"href":4148,"rel":4149},"http://jshint.com/docs/options/",[544]," JSHint has both ‘enforcing’ and ‘relaxing’ rules. While setting a ‘enforcing’\nrule to true turns it on, setting a ‘relaxing’ rule to true deactivates it.",[906,4152,4153],{},"More often than not, using JSHint can be frustrating. For example, we had ‘maxdepth’",[18,4155,4156,4157,4160,4161,4164,4165,4168],{},"set to 3, meaning a maximum of three nested blocks of code was allowed. In case one of those blocks was a ‘",[910,4158,4159],{},"for … in","‘\nstatement, JSHint would (correctly) complain that its body should be wrapped in an ‘",[910,4162,4163],{},"if(obj.hasOwnProperty(key))","\n‘-check to filter out unwanted properties. However, doing so meant introducing another nested block, and if that pushed\nthe total depth beyond ‘",[910,4166,4167],{},"maxdepth","‘, JSHint would fail the build. The solution was usually to introduce private helper\nfunctions, which can make otherwise trivial code difficult to read (since you have to skip blocks of code).",[18,4170,4171],{},"Of course, that is not really the fault of JSHint (seeing that it only did what it was told to do), but it was a rather\nbig annoyance that caused us to re-evaluate our JavaScript linting practices in the first place.",[903,4173,4174],{},[906,4175,4176,4177,4182,4183,4188],{},"Being a fork of JSLint, JSHint has the same license containing the\ninfamous ",[540,4178,4181],{"href":4179,"rel":4180,"title":4181},"http://en.wikipedia.org/wiki/JSLint#License",[544],"JSLint License"," ‘Good, not evil’ statement.\nWhile we understand its humorous intent (and being\na ",[540,4184,4187],{"href":4185,"rel":4186,"title":4187},"https://synyx.de/unternehmen/verantwortung_csr/",[544],"fairly social responsible company",",\nwe wholeheartedly support it), we were worried that some corporate lawyer might not approve of our use of a tool bound\nto such a license.",[523,4190,4034],{"id":4191},"jslint",[18,4193,4194],{},"After JSHint we decided to evaluate the old guy in the gang, JSLint. It was the first linting tool for JavaScript, and\nit feels like that. From our opinion JSLint has two major problems, besides the license (see JSHint):",[903,4196,4197,4200,4203],{},[906,4198,4199],{},"The website of JSLint is very old-school and does not contain any explanations of the ruleset or any information to\nget a link between the error messages provided by JSLint and the problem in the code. So you have to search through\nthe internet to find any third-party explanation that will help you to fix the problem.",[906,4201,4202],{},"Some of the rules of JSLint are, at least, strangely named. There is a rule to forbid (or allow, if deactivated)\n‘stupidity’. The project’s web page does not provide any explanation (again) – resorting to Google, we found out that\n‘stupidity’ referred to the usage of synchronous functions in Node.js’s file system module.",[906,4204,4205],{},"The rule set is very strict and, when you look through the GitHub issues and pull requests, it is very hard to\nparticipate in the JSLint project. That’s why the community is very small and there are a lot more people active in\nJSHint and other projects and bring in their ideas there. Maybe that is why JSLint does not provide a rule similar to\n‘latedef’ from JSHint or ‘no-use-before-define’ from ESLint. Without this rule it is very hard to structure your\ncode with private named-functions at the end without assigning the function to a variable at the start (and that\nwould not be what we want).",[523,4207,4209],{"id":4208},"closure-linter","Closure Linter",[18,4211,4212],{},"The Closure Linter is part of Google’s Closure tool set. It was designed for internal use and provides very little\noptions for customization. Since following the rules enforced by it seems to be mandatory within Google, that is\ncertainly an acceptable practice. However, since it is (for example) not possible to change the default maximum line\nlength of 80 characters, we quickly decided not to look into the tool any more.",[523,4214,4049],{"id":4215},"eslint",[18,4217,4218],{},"When looking at ESLint, we were quick to decide that we might be looking at a potential winner:",[903,4220,4221,4224,4227],{},[906,4222,4223],{},"While the project is (by far) the youngest (or as others might put it: the least mature) of the four tools we looked\nat, it is also the best maintained. 100+ contributors on Github and a roughly monthly release schedule speak for\nthemselves.",[906,4225,4226],{},"Applying it to our example web app, we were surprised to find out that it was sufficient to write about ten lines of\nconfiguration to perform the same amount of checks that required around a hundred lines in JSHint. Of course, that\nmight only mean that we have the same idea of quality JavaScript code as the tool’s authors, but nevertheless it meant\nthat the tool would be quite easy to adopt into our development process.",[906,4228,4229],{},"The output is quite handy: It prints both a one-line human readable error message as well as an error code for a\nquick lookup in the documentation.",[523,4231,4233],{"id":4232},"evaluation","Evaluation",[18,4235,4236],{},"The criteria from above has been weighted from five to 15, from not important to important, and the tools got a 0, 0.5\nor 1 if it does not, almost or absolute fulfill the criterion.",[18,4238,4239],{},[960,4240],{"alt":4241,"src":4242},"\"jsLinting\"","https://media.synyx.de/uploads//2015/02/jsLinting2.png",[523,4244,2871],{"id":2870},[18,4246,4247],{},"As you can see in the image above, ESLint proved to be the winner of our evaluation. We decided that its major flaw, the\npotential immaturity, was acceptable to us since by its nature, it would only be used during (internal) development. The\nease of use, both because of the robust and reasonable default rule set and the high-quality documentation, outweighed\nany concern by far. We are looking forward to adopt ESLint into our development tool chain over the coming weeks and\nmonths!",{"title":47,"searchDepth":69,"depth":69,"links":4249},[4250,4251,4252,4253,4254,4255,4256],{"id":4065,"depth":69,"text":4066},{"id":4137,"depth":69,"text":4058},{"id":4191,"depth":69,"text":4034},{"id":4208,"depth":69,"text":4209},{"id":4215,"depth":69,"text":4049},{"id":4232,"depth":69,"text":4233},{"id":2870,"depth":69,"text":2871},[810,4258],"open-source-blog","2015-02-03T09:45:38","In our internal JavaScript ‘User Group’ (called JS-Posse in honour of the\\nlegendary ‘The Java Posse‘ by Dick Wall, Chet Haase et al.), we recently decided to evaluate\\nalternatives to our current JavaScript linting standart, JSHint. Although well established by now among different\\ndevelopment teams across synyx, using it never felt 100% comfortable. A quick Google search left\\nus with three alternatives:","https://synyx.de/blog/javascript-linting-tool-evaluation/",{},"/blog/javascript-linting-tool-evaluation",{"title":4004,"description":4265},"In our internal JavaScript ‘User Group’ (called JS-Posse in honour of the\nlegendary ‘The Java Posse‘ by Dick Wall, Chet Haase et al.), we recently decided to evaluate\nalternatives to our current JavaScript linting standart, JSHint. Although well established by now among different\ndevelopment teams across synyx, using it never felt 100% comfortable. A quick Google search left\nus with three alternatives:","blog/javascript-linting-tool-evaluation",[1570,4215,4232,1216,4268,4137,4191,4269,828],"js","linting","In our internal JavaScript ‘User Group’ (called JS-Posse in honour of the legendary ‘The Java Posse‘ by Dick Wall, Chet Haase et al.), we recently decided to evaluate alternatives to…","QcXdToJb-JTf6znqD4o0GbdwGfu2XcNA6y-tOvH75ck",{"id":4273,"title":4274,"author":4275,"body":4276,"category":4522,"date":4523,"description":4524,"extension":815,"link":4525,"meta":4526,"navigation":303,"path":4527,"seo":4528,"slug":4280,"stem":4530,"tags":4531,"teaser":4538,"__hash__":4539},"blog/blog/my-first-time-on-a-conference-with-synyx.md","My first time on a conference with synyx",[3736],{"type":11,"value":4277,"toc":4520},[4278,4281,4288,4294,4297,4300,4317,4331,4346,4378,4419,4434,4437,4452,4471,4474,4477,4494,4497],[14,4279,4274],{"id":4280},"my-first-time-on-a-conference-with-synyx",[18,4282,4283,4284,4287],{},"I am a fresh employee at synyx. I started early in 2013 to work for the company and the",[910,4285,4286],{},"Devoxx 2013"," in Antwerp was my\nfirst conference with my new colleagues. Everything started around august when a colleague asked „Whoooo want to go to\nthe Devoxx in November. Are you in? And you?“, he was so enthusiastic that I thought: “Hm first conference with these\nguys? One week with them in another country? And that on my birthday? Let’s do it!”.",[18,4289,4290,4291,4293],{},"Till November nothing big happens. I searched through the Devoxx site for speakers and talks, but not so much. I wanted\nto be free and just do what I want to do on the Devoxx and be ",[910,4292,1799],{},", of course. Then, one week before the Devoxx, I\ntalked to my colleague to know when the train leaves, where we are going to sleep, where to get the tickets and so on.\nAt this point I realized that synyx has everything organized and well planed. Good guy synyx!",[18,4295,4296],{},"When we arrived at the main station in Frankfurt the first cuba libre was ready to go. Of course with ice and lime. Yes\nwe had ice. We also shared some with friends we met before. We chatted on the train about talks, speakers, the company,\nfuture projects, our new office and challenges in some of our projects. It was fun to speak about things that bother you\nin this different atmosphere than our office or meeting rooms. It was very relaxing and fun.",[18,4298,4299],{},"The next day was the first day on the Devoxx. We started early to get our access keys and traveled there via subway. Its\na nice subway with a ‘nice’ smell, something like mushrooms.",[18,4301,4302,4303,4308,4309,4312,4313,4316],{},"One of the first talks I listened to was ",[540,4304,4307],{"href":4305,"rel":4306},"http://fhornain.wordpress.com/2013/11/12/devoxx13-java-ee-7-whats-new-in-the-java-ee-platform/http://",[544],"**Java EE 7: What’s New in the Java EE Platform\n**",". ",[910,4310,4311],{},"Argun\nGupta"," and ",[910,4314,4315],{},"Antonio Goncalves"," talked about new features of Java EE7. It was very interesting and a clear presentation\nwith good speakers. They talked about 3 hours, so it was good that they also did a lot of jokes on the stage especially\nwhen questions arrived like “why do you do not use spring? It has every feature you represent since two years.” Good\nSpeakers, good presentation. I learned a lot about Java EE in general.",[18,4318,4319,4322,4323,4330],{},[910,4320,4321],{},"David Gageot"," showed us in a quick 30 minutes live coding session called ",[540,4324,4327],{"href":4325,"rel":4326},"http://www.devoxx.be/dv13-david-gageot.html?presId=3108",[544],[1030,4328,4329],{},"From Legacy to Cloud Under an Hour – Live\nCoding"," how easy it is to take old nasty unreadable code with\nabout a million of if/else statements and other dirty stuff in it and a good IDEA to get a nice clean code without\nduplicates and all the if/else and put it on the cloud at the end. Very impressive to see that in 30 minutes. He must\nhave trained a lot! Notice to me: Take time for your IDEA and learn what you can do with it, even if you think you know\na lot.",[18,4332,4333,4334,4337,4338,4345],{},"At the second day I was excited about the talk from ",[910,4335,4336],{},"José Paumard"," called ",[540,4339,4342],{"href":4340,"rel":4341},"http://www.devoxx.be/dv13-jos-paumard.html?presId=3540",[544],[1030,4343,4344],{},"Autumn Collection: from iterable to\nlambdas, streams and collectors"," because this stuff you are\nusing and will use all the time when programming. It gave a real clear view on lambdas, streams and what you can do with\nit. The syntax looked quite nice, if you take care of it. Do not start to use it for everything and make things more\ncomplicated than they already are. Let’s see how it will be in future projects with Java 8. It feels like parallel\nprogramming will be made available and easier for a wider mass of developers.",[18,4347,4348,4349,4356,4357,4360,4361,146,4366,4371,4372,4377],{},"The next talk I watched was ",[540,4350,4353],{"href":4351,"rel":4352},"http://www.devoxx.be/dv13-matt-raible.html?presId=3648",[544],[1030,4354,4355],{},"The Modern Java Web Developer",".\n",[910,4358,4359],{},"Matt Raible"," is a very good speaker with a lot of self irony and sarcasm. Which makes the talk even more interesting.\nHe talked about the modern solutions of web development\nlike ",[960,4362],{"alt":4363,"src":4364,"title":4365},"\"20131112_133433\"","http://angularjs.org/","Angular JS",[540,4367,4370],{"href":4368,"rel":4369},"http://jquery.com/",[544],"jQuery",",\nCSS3, ",[540,4373,4376],{"href":4374,"rel":4375},"http://www.html5rocks.com/en/",[544],"HTML5"," and so on. It was a great overview over the preferred technologies for web\ndevelopers. At the end of his talk the showed us a video of him coding a complete dashboard application at one\nafternoon. Of course with a lot of copy paste. But it was nice to see the problems he got stuck, how he fixed it and how\npowerful and easy to use modern web frameworks and libraries are.",[18,4379,4380,4381,4384,4385,4392,4393,4396,4397,4400,4401,2610,4404,4312,4409,4414,4415,4418],{},"When I wrote this blog entry about the third day I could not remember what talks were really good at that day, besides\nthe talks of ",[910,4382,4383],{},"Martijn Verburg",". The problem was not that they were bad, but maybe not so forceful. On the other hand I\nwatched ",[540,4386,4389],{"href":4387,"rel":4388},"http://www.devoxx.be/dv13-chet-haase.html?presId=3193",[544],[1030,4390,4391],{},"Patterns Shmatterns"," by ",[910,4394,4395],{},"Chet Haase",". And maybe that\nmakes me forget all the other talks. It was a ",[910,4398,4399],{},"fun quickie"," about patterns and their ‘pendant’. Great show, great funny\nface at the front. Definitely one talk to watch between other real talks to relax. The talks from ",[910,4402,4403],{},"Martijn Verburg:",[540,4405,4408],{"href":4406,"rel":4407},"http://www.devoxx.be/dv13-martijn-verburg.html?presId=3214",[544],"*\n*The Habits of Highly Effective Technical Teams**",[540,4410,4413],{"href":4411,"rel":4412},"http://www.devoxx.be/dv13-martijn-verburg.html?presId=3223",[544],"*\n*The Bleeding Edge**"," were both very good. The first named\ntalk was one of my highlights on the Devoxx, because it forced me to think about companies and how they treat their\nemployees and how they should treat them. For example why companies do not gave their employees rights to the productive\nsystem for the project they work on ",[910,4416,4417],{},"and so on and so forth",". A lot of questions where thrown into the room which I\nnever really asked myself and their was one moment where I realized that synyx is making a good job. Definitely a talk\nemployees and employer should watch and think about their process.",[18,4420,4421,4425,4426,4429,4430,4433],{},[960,4422],{"alt":4423,"src":4424},"\"20131111_231326\"","https://media.synyx.de/uploads//2013/12/20131111_231326.jpg","\nThis was not only a effective day at the Devoxx. it was also my birthday. So we went for lunch this evening with\neveryone and then went into a student bar somewhere in Antwerp. It was really funny to go into a small student bar with\nround about 13 people on an evening where they had a 90’s party. Of course we knew all the great 90’s songs like Barbie\nGirl (O",[910,4427,4428],{},"o). But we were not prepared for it and the girls with their shards, yeah shards. Sometimes it is funny if you\nhave a close look at it. After that ‘crazy’ party we moved on to the _Beer Central"," to taste all their 300+ beer.\nLet’s say we ",[910,4431,4432],{},"almost"," did it. Great but different birthday than the years before. Thanks synyx!",[18,4435,4436],{},"At the last two days of the conference I watched a lot of talks, just one after another and running from room to room.\nTwo of them I do remind very well, because they where my points of lights these days.",[18,4438,4439,4440,4443,4444,4451],{},"First of all another highlight of my Devoxx week was the talk from ",[910,4441,4442],{},"Joey Cieplinski"," with the title ",[540,4445,4448],{"href":4446,"rel":4447},"http://www.devoxx.be/dv13-joe-cieplinski.html?presId=3330",[544],[1030,4449,4450],{},"Business\nStrategies for Small Independent Developers"," he talked\nabout premium and none premium applications for mobile devices and his view to make higher price applications. Then to\nconcentrate on the 10% of customers that care about your product and do not see it as a throwaway piece of software to\nbuy for less then 1€. This talk confirmed me in my opinion to create quality software and get what you earn for a good\nproduct and concentrate of the concerns of real customers and their problems. Inspiring talk, thank you for sharing it.",[18,4453,4454,4461,4462,4465,4466,4470],{},[540,4455,4458],{"href":4456,"rel":4457},"http://www.devoxx.be/dv13-alain-regnier.html?presId=3733",[544],[1030,4459,4460],{},"Introduction to Google Glass"," from ",[910,4463,4464],{},"Alain Regnier","was my\nlast talk I visited. It was a demonstration about what can be done with the glass and what could be done with it in the\nfuture. Alain also explained a lot on how Google act by spreading the glass and what you have to do to get the\nglass. ",[960,4467],{"alt":4468,"src":4469},"\"20131115_105017\"","https://media.synyx.de/uploads//2013/12/20131115_105017.jpg","\nHe explained how to program with the Mirror API and his hopes of getting the GDK (Glass Development Kit) soon. Google\nGlass seems to be a product which can change parts of our lives, but for this it needs a real case in which it will be\nnecessary to have one. I am excited to see what will be possible in the future.",[18,4472,4473],{},"And at the end a small summary.",[18,4475,4476],{},"Antwerp:",[903,4478,4479,4482,4485,4488,4491],{},[906,4480,4481],{},"The subway smells like mushrooms.",[906,4483,4484],{},"The subway is small and carries a lot of people.",[906,4486,4487],{},"Awesome and horrible beer at once.",[906,4489,4490],{},"Beautiful main train station.",[906,4492,4493],{},"They love mayonnaise.",[18,4495,4496],{},"Devoxx:",[903,4498,4499,4502,4505,4508,4511,4514,4517],{},[906,4500,4501],{},"Great speakers with very interesting talks.",[906,4503,4504],{},"Nice People.",[906,4506,4507],{},"Learned a lot of new stuff.",[906,4509,4510],{},"Raspberry Pis will rule the world.",[906,4512,4513],{},"Crab sandwiches are delicious. Yeah! Ate a lot of them.",[906,4515,4516],{},"Tuna salad with only tuna are not that fun.",[906,4518,4519],{},"Belgian fries are awesome.",{"title":47,"searchDepth":69,"depth":69,"links":4521},[],[811],"2013-12-06T08:59:44","I am a fresh employee at synyx. I started early in 2013 to work for the company and theDevoxx 2013 in Antwerp was my\\nfirst conference with my new colleagues. Everything started around august when a colleague asked „Whoooo want to go to\\nthe Devoxx in November. Are you in? And you?“, he was so enthusiastic that I thought: “Hm first conference with these\\nguys? One week with them in another country? And that on my birthday? Let’s do it!”.","https://synyx.de/blog/my-first-time-on-a-conference-with-synyx/",{},"/blog/my-first-time-on-a-conference-with-synyx",{"title":4274,"description":4529},"I am a fresh employee at synyx. I started early in 2013 to work for the company and theDevoxx 2013 in Antwerp was my\nfirst conference with my new colleagues. Everything started around august when a colleague asked „Whoooo want to go to\nthe Devoxx in November. Are you in? And you?“, he was so enthusiastic that I thought: “Hm first conference with these\nguys? One week with them in another country? And that on my birthday? Let’s do it!”.","blog/my-first-time-on-a-conference-with-synyx",[1682,4532,4533,4534,4535,1685,4536,4537],"devoxx","gogle-glass","java-8","java-ee7","modern-web-development","premium-apps","I am a fresh employee at synyx. I started early in 2013 to work for the company and the Devoxx 2013 in Antwerp was my first conference with my new colleagues.…","o_aFlRO4Z2neys_7nDR-63csn2MLWAYQgg8F4ktsHls",{"id":4541,"title":4542,"author":4543,"body":4545,"category":4910,"date":4911,"description":4552,"extension":815,"link":4912,"meta":4913,"navigation":303,"path":4914,"seo":4915,"slug":4549,"stem":4916,"tags":4917,"teaser":4924,"__hash__":4925},"blog/blog/how-sysadmins-monitor-your-java-application-with-jmx.md","How Sysadmins monitor your Java application with JMX",[4544],"ruessel",{"type":11,"value":4546,"toc":4901},[4547,4550,4553,4561,4565,4569,4572,4583,4589,4592,4595,4605,4608,4618,4627,4652,4656,4672,4676,4679,4700,4747,4752,4767,4784,4822,4827,4885,4890,4898],[14,4548,4542],{"id":4549},"how-sysadmins-monitor-your-java-application-with-jmx",[18,4551,4552],{},"some time ago Aljona showed",[18,4554,4555],{},[540,4556,4560],{"href":4557,"rel":4558,"title":4559},"http://blog.synyx.de/2012/05/how-to-monitor-and-manage-your-java-application-with-jmx/",[544],"JMX-Blog by Aljona ","how to monitor and manage your java application with jmx",[523,4562,4564],{"id":4563},"im-going-to-show-how-you-can-make-use-of-jmx-from-the-viewpoint-of-a-sysadmin","I’m going to show, how you can make use of JMX from the viewpoint of a sysadmin.",[1503,4566,4568],{"id":4567},"initial-point","initial point:",[18,4570,4571],{},"You have a Java-application deployed in an applicationserver like JBoss or Tomcat and you want to monitor the health of\nthis application(including the applicationserver and the Java-virtual-machine it is running in) with a tool like\nNagios.",[1503,4573,4575,4576,4582],{"id":4574},"use-jolokia-on-the-server-side","Use ",[540,4577,4581],{"href":4578,"rel":4579,"title":4580},"http://jolokia.org",[544],"JMX on Capsaicin","Jolokia"," on the server-side!",[18,4584,4585],{},[960,4586],{"alt":4587,"src":4588},"\"jolokia\"","https://media.synyx.de/uploads//2013/02/jolokia.png",[18,4590,4591],{},"Why do i have to use an extra Agent for this? My applicationserver is able to use Mbeans.",[18,4593,4594],{},"Of course you don’t have to, but Jolokia is a HTTP/JSON bridge, which means jolokia can be accessed via HTTP and\nresponds with JSON, that is cool, isn’t it?",[18,4596,4597,4598,4604],{},"There are some more advantages like bulk-requests and it\nsupports ",[540,4599,4603],{"href":4600,"rel":4601,"title":4602},"https://web.archive.org/web/20130301163513/http://square.github.com:80/cubism/",[544],"cubism on github","cubism.js","\nsince version 1.0.5 (Jonathan uses this to get some nicely visualized charts) … and it’s fast.",[18,4606,4607],{},"Just deploy it into your applicationserver it isn’t that big.",[18,4609,4610,4611,4617],{},"As a sysadmin i don’t want to code Java nor do i want to have a JVM running on my Nagios-server, i’m used to use perl (\njust to satisfy any prejudices), so i stumbled\nover ",[540,4612,4616],{"href":4613,"rel":4614,"title":4615},"http://labs.consol.de/jmx4perl/",[544],"Jmx4Perl on ConsolLabs ","jmx4perl"," and decided to make use of this.",[18,4619,4620,4621,4626],{},"I installed ",[540,4622,4616],{"href":4623,"rel":4624,"title":4625},"http://search.cpan.org/~roland/jmx4perl/",[544],"Jmx4Perl on CPAN"," and read the documentation.",[42,4628,4632],{"className":4629,"code":4630,"language":4631,"meta":47,"style":47},"language-shell shiki shiki-themes github-light github-dark","\ncpan[1]> install JMX::Jmx4Perl\n\n","shell",[22,4633,4634,4638],{"__ignoreMap":47},[51,4635,4636],{"class":53,"line":54},[51,4637,304],{"emptyLinePlaceholder":303},[51,4639,4640,4643,4646,4649],{"class":53,"line":69},[51,4641,4642],{"class":61},"cpan[1]",[51,4644,4645],{"class":65},"> ",[51,4647,4648],{"class":103},"install",[51,4650,4651],{"class":103}," JMX::Jmx4Perl\n",[1503,4653,4655],{"id":4654},"the-tools-brought-along-by-jmx4perl-are-really-very-useful","The tools brought along by jmx4perl are really very useful:",[4657,4658,4659,4666],"ol",{},[906,4660,4661,4662,4665],{},"The commandlinetool ",[1030,4663,4664],{},"j4psh"," is awesome, you can browse the JMX Mbeans in color by using “cd” and “ls” with “cat”\nyou can get the value of an attribute… tab-completion as a matter of course.",[906,4667,4668,4669,4671],{},"With the commandlinetool ",[1030,4670,4616],{}," you can easily test requests and get responses in various forms.",[1503,4673,4675],{"id":4674},"time-to-hack","Time to hack:",[18,4677,4678],{},"First Nagios-test we assemble is for monitoring heapspace of the JVM, not only because it’s often referenced in the\ndocumentation but rather because thats often a useful information to identify memory leaks and to know when it’s time to\nrestart the application(server). But you can follow most of the following steps for many other use cases too.",[4657,4680,4681,4684,4687,4694,4697],{},[906,4682,4683],{},"be sure you got the relevant permissions in jolokia (jolokia-access.xml)",[906,4685,4686],{},"search for older Nagios-Plugins and c’n’p the generic parts…",[906,4688,4689,4690],{},"search for the Mbean with j4psh or use type search of jolokia and look for interesting attributes/operations\n",[960,4691],{"alt":4692,"src":4693},"\"j4psh_screenshot\"","https://media.synyx.de/uploads//2013/02/j4psh_screenshot.png",[906,4695,4696],{},"test the request you have in your mind with e.g jmx4perl",[906,4698,4699],{},"canonicalize the request",[42,4701,4705],{"className":4702,"code":4703,"language":4704,"meta":47,"style":47},"language-perl shiki shiki-themes github-light github-dark","my $request = new JMX::Jmx4Perl::Request({\n type => READ,\n mbean => \"java.lang:type=Memory\",\n attribute => \"HeapMemoryUsage\",\n });\n#print(Dumper($request));\nmy $response = $jmx->request($request);\n#print(Dumper($response));\n","perl",[22,4706,4707,4712,4717,4722,4727,4732,4737,4742],{"__ignoreMap":47},[51,4708,4709],{"class":53,"line":54},[51,4710,4711],{},"my $request = new JMX::Jmx4Perl::Request({\n",[51,4713,4714],{"class":53,"line":69},[51,4715,4716],{}," type => READ,\n",[51,4718,4719],{"class":53,"line":82},[51,4720,4721],{}," mbean => \"java.lang:type=Memory\",\n",[51,4723,4724],{"class":53,"line":205},[51,4725,4726],{}," attribute => \"HeapMemoryUsage\",\n",[51,4728,4729],{"class":53,"line":241},[51,4730,4731],{}," });\n",[51,4733,4734],{"class":53,"line":250},[51,4735,4736],{},"#print(Dumper($request));\n",[51,4738,4739],{"class":53,"line":256},[51,4740,4741],{},"my $response = $jmx->request($request);\n",[51,4743,4744],{"class":53,"line":262},[51,4745,4746],{},"#print(Dumper($response));\n",[4657,4748,4749],{"start":262},[906,4750,4751],{},"dereference the response as you need",[42,4753,4755],{"className":4702,"code":4754,"language":4704,"meta":47,"style":47},"my $used_memory=$response->value()->{'used'};\nmy $max_memory=$response->value()->{'max'};\n",[22,4756,4757,4762],{"__ignoreMap":47},[51,4758,4759],{"class":53,"line":54},[51,4760,4761],{},"my $used_memory=$response->value()->{'used'};\n",[51,4763,4764],{"class":53,"line":69},[51,4765,4766],{},"my $max_memory=$response->value()->{'max'};\n",[4657,4768,4769,4772,4775,4778,4781],{"start":282},[906,4770,4771],{},"start a heated debate about sensible values for warning and critical",[906,4773,4774],{},"deploy your Nagios-plugin",[906,4776,4777],{},"copy the script into your plugin-folder of your Nagios-server",[906,4779,4780],{},"install all missing libraries on the Nagios-server",[906,4782,4783],{},"define the command in commands.cfg",[42,4785,4787],{"className":44,"code":4786,"language":46,"meta":47,"style":47},"define command{\n command_name check_jmx4perl_heapspace.pl\n command_line $USER1$/check_jmx4perl_heapspace.pl $HOSTADDRESS$ $ARG1$\n}\n",[22,4788,4789,4796,4804,4818],{"__ignoreMap":47},[51,4790,4791,4793],{"class":53,"line":54},[51,4792,1354],{"class":61},[51,4794,4795],{"class":103}," command{\n",[51,4797,4798,4801],{"class":53,"line":69},[51,4799,4800],{"class":61}," command_name",[51,4802,4803],{"class":103}," check_jmx4perl_heapspace.pl\n",[51,4805,4806,4809,4812,4815],{"class":53,"line":82},[51,4807,4808],{"class":61}," command_line",[51,4810,4811],{"class":65}," $USER1$",[51,4813,4814],{"class":103},"/check_jmx4perl_heapspace.pl",[51,4816,4817],{"class":65}," $HOSTADDRESS$ $ARG1$\n",[51,4819,4820],{"class":53,"line":205},[51,4821,253],{"class":65},[4657,4823,4824],{"start":241},[906,4825,4826],{},"make use of the new command in your services.cfg (or whatever you’ve called your file)",[42,4828,4830],{"className":44,"code":4829,"language":46,"meta":47,"style":47},"\ndefine service{\n use remote-service\n host_name jolokia-host\n service_description HeapSpace of JavaApp\n check_command check_jmx4perl_heapspace.pl!8084\n}\n\n",[22,4831,4832,4836,4843,4851,4859,4873,4881],{"__ignoreMap":47},[51,4833,4834],{"class":53,"line":54},[51,4835,304],{"emptyLinePlaceholder":303},[51,4837,4838,4840],{"class":53,"line":69},[51,4839,1354],{"class":61},[51,4841,4842],{"class":103}," service{\n",[51,4844,4845,4848],{"class":53,"line":82},[51,4846,4847],{"class":61}," use",[51,4849,4850],{"class":103}," remote-service\n",[51,4852,4853,4856],{"class":53,"line":205},[51,4854,4855],{"class":61}," host_name",[51,4857,4858],{"class":103}," jolokia-host\n",[51,4860,4861,4864,4867,4870],{"class":53,"line":241},[51,4862,4863],{"class":61}," service_description",[51,4865,4866],{"class":103}," HeapSpace",[51,4868,4869],{"class":103}," of",[51,4871,4872],{"class":103}," JavaApp\n",[51,4874,4875,4878],{"class":53,"line":250},[51,4876,4877],{"class":61}," check_command",[51,4879,4880],{"class":103}," check_jmx4perl_heapspace.pl!8084\n",[51,4882,4883],{"class":53,"line":256},[51,4884,253],{"class":65},[4657,4886,4887],{"start":355},[906,4888,4889],{},"lean back while watching the heapspace grow…",[18,4891,4892],{},[540,4893,4897],{"href":4894,"rel":4895,"title":4896},"https://github.com/zivis/Scripts/blob/master/check_jmx4perl_heapspace.pl",[544],"check_jmx4perl_heapspace.pl on github","get the complete Nagios-plugin from github",[799,4899,4900],{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":47,"searchDepth":69,"depth":69,"links":4902},[4903],{"id":4563,"depth":69,"text":4564,"children":4904},[4905,4906,4908,4909],{"id":4567,"depth":82,"text":4568},{"id":4574,"depth":82,"text":4907},"Use Jolokia on the server-side!",{"id":4654,"depth":82,"text":4655},{"id":4674,"depth":82,"text":4675},[809],"2013-02-07T00:48:50","https://synyx.de/blog/how-sysadmins-monitor-your-java-application-with-jmx/",{},"/blog/how-sysadmins-monitor-your-java-application-with-jmx",{"title":4542,"description":4552},"blog/how-sysadmins-monitor-your-java-application-with-jmx",[1570,1405,4918,4919,4920,4921,4922,4704,4923],"jboss","jmx","monitoring","nagios","open-source","tomcat","some time ago Aljona showed how to monitor and manage your java application with jmx I’m going to show, how you can make use of JMX from the viewpoint of…","MFqu5fP8lFxCs1VVecsJZpCxYTd8MOKoaLxMErncmaw",{"id":4927,"title":4928,"author":4929,"body":4931,"category":5004,"date":5005,"description":4938,"extension":815,"link":5006,"meta":5007,"navigation":303,"path":5008,"seo":5009,"slug":5010,"stem":5011,"tags":5012,"teaser":5013,"__hash__":5014},"blog/blog/clean-code-development-prinzipien-und-praktiken-zur-steigerung-der-software-qualitat.md","Clean Code Development – Prinzipien und Praktiken zur Steigerung der Software Qualität",[4930],"sommer",{"type":11,"value":4932,"toc":5002},[4933,4936,4939,4942,4945,4950,4953,4958,4961,4964,4969,4972,4975,4980,4983,4986,4989,4992,4995],[14,4934,4928],{"id":4935},"clean-code-development-prinzipien-und-praktiken-zur-steigerung-der-software-qualität",[18,4937,4938],{},"Software Qualität ist ein Trendthema aber wie erreicht man eine hohe",[18,4940,4941],{},"Qualität? Reichen gängige Instrumente wie UnitTests und Code-Reviews aus?",[18,4943,4944],{},"Clean Code Development bietet keine fertigen Problemlösungen sondern fasst Prinzipien und Praktiken zusammen und sieht\neine stufenweise Einführung derer vor. Ohne auf die einzelnen Grade, welche den Weg zum Clean Code Developer\nbeschreiben, einzugehen möchte ich hier ein paar ausgewählte Prinzipien kurz vorstellen.",[18,4946,4947],{},[1030,4948,4949],{},"Don’t Repeat Yourself (DRY)",[18,4951,4952],{},"Code Duplizierung sollte vermieden werden, da Anpassungen an mehreren stellen erfolgen müssen um Konsistenz zu\ngewährleisten. Eigentlich ein alter Hut aber Copy & Paste ist in der Praxis weit verbreitet.",[18,4954,4955],{},[1030,4956,4957],{},"Keep it simple, stupid (KISS)",[18,4959,4960],{},"Mach es nicht komplizierter als notwendig.",[18,4962,4963],{},"Ja, auch wenn es dir uncool und langweilig erscheint!",[18,4965,4966],{},[1030,4967,4968],{},"Favour Composition over Inheritance (FCoI)",[18,4970,4971],{},"Bei der Vererbung entstehen Abhängikeiten von den Subklassen zu den Elternklassen, was das Austauschen der\nFunktionalität zur Laufzeit erschwert. Wird die Vererbung inflationär verwendet, kann dies ein System schnell unnötig\nkomplex und schlecht testbar werden lassen.",[18,4973,4974],{},"Bei der Komposition verwendet eine Klasse eine andere, was die Abhängigkeiten reduziert und die Testbarkeit wesentlich\nvereinfacht.",[18,4976,4977],{},[1030,4978,4979],{},"You Ain’t Gonna Need It (YAGNI)",[18,4981,4982],{},"Dieses Prinzip bedeutet soviel wie “implementiere nichts was du nicht brauchst”. Das klingt logisch aber dennoch hab ich\nselbst schon gegen dieses Prinzip verstoßen.",[18,4984,4985],{},"In der Praxis wird versucht Software auf künftige Anforderungen vorzubereiten aber oft stellt sich später heraus, dass\nsich die ursprünglich erwarteten Anforderungen tatsächlich nie ergeben haben.",[18,4987,4988],{},"Statt dessen ergeben sich natürlich ganz andere Anforderungen die nicht berücksichtigt wurden und deren Umsetzung jetzt\ndurch unnötigen Balast behindert werden.",[18,4990,4991],{},"Die Praktiken des Clean Code Developments sind Methoden und Techniken die ständig eingesetzt werden sollten und umfassen\nzum Beispiel das Issue Tracking, automatisierte Integrationstests, Continuous Integration, statische Codeanalysen aber\nauch das Weitergeben von Erfahrungen und die Teilnahme an Fachveranstaltungen.",[18,4993,4994],{},"Mehr zu Clean Code Development gibt es unter",[18,4996,4997],{},[540,4998,4999],{"href":4999,"rel":5000,"title":5001},"http://www.clean-code-developer.de",[544],"clean-code-developer",{"title":47,"searchDepth":69,"depth":69,"links":5003},[],[810],"2012-08-16T09:57:29","https://synyx.de/blog/clean-code-development-prinzipien-und-praktiken-zur-steigerung-der-software-qualitat/",{},"/blog/clean-code-development-prinzipien-und-praktiken-zur-steigerung-der-software-qualitat",{"title":4928,"description":4938},"clean-code-development-prinzipien-und-praktiken-zur-steigerung-der-software-qualitat","blog/clean-code-development-prinzipien-und-praktiken-zur-steigerung-der-software-qualitat",[1570,3137],"Software Qualität ist ein Trendthema aber wie erreicht man eine hohe Qualität? Reichen gängige Instrumente wie UnitTests und Code-Reviews aus? Clean Code Development bietet keine fertigen Problemlösungen sondern fasst Prinzipien…","qanNmRX3aNPeWTNpe7MMGWO8r9OdqTQQpJRo9UX9hwo",{"id":5016,"title":5017,"author":5018,"body":5019,"category":5652,"date":5653,"description":5654,"extension":815,"link":5655,"meta":5656,"navigation":303,"path":5657,"seo":5658,"slug":5023,"stem":5659,"tags":5660,"teaser":5663,"__hash__":5664},"blog/blog/migrating-data-with-liquibase.md","Migrating data with Liquibase",[4006],{"type":11,"value":5020,"toc":5650},[5021,5024,5027,5030,5033,5036,5083,5086,5165,5168,5182,5185,5188,5248,5251,5289,5292,5338,5341,5344,5358,5361,5427,5430,5433,5491,5494,5497,5500,5577,5580,5583,5625,5628,5642,5645,5648],[14,5022,5017],{"id":5023},"migrating-data-with-liquibase",[18,5025,5026],{},"Recently, we started integrating Liquibase as a database schema migration tool into most of my team’s projects, for both\nnew from-scratch projects and already existing ones. Liquibase is great because it allows us to use an SCM tool like\nGit to manage different revisions of an applications database schema – or more specifically, the changes required to\nmigrate the database schema from one revision to another.",[18,5028,5029],{},"While migrating database schemas seems like a pretty straight-forward task at the beginning, things get more\ncomplicated as soon as you want to roll back schema changes without dropping your database (and then rebuilding it).\nLiquibase also supports migrating your data across schema changes, in both directions. But lets start with the basics.",[18,5031,5032],{},"For this example, I only used the Liquibase command line interface, along with the basic MySQL command line client. Of\ncourse, Liquibase also integrates nicely with Maven (as a Maven goal) or Spring (as a bean that executes during context\ninitialization).",[18,5034,5035],{},"I start with a very basic table called „Person“, consisting only of an ID (primary key) and a name:",[42,5037,5039],{"className":3419,"code":5038,"language":3421,"meta":47,"style":47},"\nmysql> describe Person;\n+-------+--------------+------+-----+---------+----------------+\n| Field | Type | Null | Key | Default | Extra |\n+-------+--------------+------+-----+---------+----------------+\n| id | bigint(20) | NO | PRI | NULL | auto_increment |\n| name | varchar(255) | NO | UNI | NULL | |\n+-------+--------------+------+-----+---------+----------------+\n2 rows in set (0.00 sec)\n\n",[22,5040,5041,5045,5050,5055,5060,5064,5069,5074,5078],{"__ignoreMap":47},[51,5042,5043],{"class":53,"line":54},[51,5044,304],{"emptyLinePlaceholder":303},[51,5046,5047],{"class":53,"line":69},[51,5048,5049],{},"mysql> describe Person;\n",[51,5051,5052],{"class":53,"line":82},[51,5053,5054],{},"+-------+--------------+------+-----+---------+----------------+\n",[51,5056,5057],{"class":53,"line":205},[51,5058,5059],{},"| Field | Type | Null | Key | Default | Extra |\n",[51,5061,5062],{"class":53,"line":241},[51,5063,5054],{},[51,5065,5066],{"class":53,"line":250},[51,5067,5068],{},"| id | bigint(20) | NO | PRI | NULL | auto_increment |\n",[51,5070,5071],{"class":53,"line":256},[51,5072,5073],{},"| name | varchar(255) | NO | UNI | NULL | |\n",[51,5075,5076],{"class":53,"line":262},[51,5077,5054],{},[51,5079,5080],{"class":53,"line":272},[51,5081,5082],{},"2 rows in set (0.00 sec)\n",[18,5084,5085],{},"Liquibase uses so-called changesets, which are XML-snippets used to describe DDL statements. They are organized in\nchange log files. The following change set is used to create a table (via the „createTable“-tag) and two columns (via\nthe „column“-tag)",[42,5087,5091],{"className":5088,"code":5089,"language":5090,"meta":47,"style":47},"language-xml shiki shiki-themes github-light github-dark","\n\u003Cdatabasechangelog xmlns=\"http://www.liquibase.org/xml/ns/dbchangelog\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemalocation=\"http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-2.0.xsd\">\n \u003Cchangeset author=\"mueller@synyx.de\" id=\"1\" runonchange=\"true\">\n \u003Ccreatetable tablename=\"Person\">\n \u003Ccolumn autoincrement=\"true\" name=\"id\" type=\"BIGINT\">\n \u003Cconstraints nullable=\"false\" primarykey=\"true\">\n \u003C/constraints>\n \u003C/column>\n \u003Ccolumn name=\"name\" type=\"VARCHAR(255)\">\n \u003Cconstraints nullable=\"false\">\n \u003C/constraints>\n \u003C/column>\n \u003C/createtable>\n \u003C/changeset>\n\u003C/databasechangelog>\n\n","xml",[22,5092,5093,5097,5102,5107,5112,5117,5122,5127,5132,5137,5142,5146,5150,5155,5160],{"__ignoreMap":47},[51,5094,5095],{"class":53,"line":54},[51,5096,304],{"emptyLinePlaceholder":303},[51,5098,5099],{"class":53,"line":69},[51,5100,5101],{},"\u003Cdatabasechangelog xmlns=\"http://www.liquibase.org/xml/ns/dbchangelog\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemalocation=\"http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-2.0.xsd\">\n",[51,5103,5104],{"class":53,"line":82},[51,5105,5106],{}," \u003Cchangeset author=\"mueller@synyx.de\" id=\"1\" runonchange=\"true\">\n",[51,5108,5109],{"class":53,"line":205},[51,5110,5111],{}," \u003Ccreatetable tablename=\"Person\">\n",[51,5113,5114],{"class":53,"line":241},[51,5115,5116],{}," \u003Ccolumn autoincrement=\"true\" name=\"id\" type=\"BIGINT\">\n",[51,5118,5119],{"class":53,"line":250},[51,5120,5121],{}," \u003Cconstraints nullable=\"false\" primarykey=\"true\">\n",[51,5123,5124],{"class":53,"line":256},[51,5125,5126],{}," \u003C/constraints>\n",[51,5128,5129],{"class":53,"line":262},[51,5130,5131],{}," \u003C/column>\n",[51,5133,5134],{"class":53,"line":272},[51,5135,5136],{}," \u003Ccolumn name=\"name\" type=\"VARCHAR(255)\">\n",[51,5138,5139],{"class":53,"line":282},[51,5140,5141],{}," \u003Cconstraints nullable=\"false\">\n",[51,5143,5144],{"class":53,"line":294},[51,5145,5126],{},[51,5147,5148],{"class":53,"line":300},[51,5149,5131],{},[51,5151,5152],{"class":53,"line":307},[51,5153,5154],{}," \u003C/createtable>\n",[51,5156,5157],{"class":53,"line":355},[51,5158,5159],{}," \u003C/changeset>\n",[51,5161,5162],{"class":53,"line":392},[51,5163,5164],{},"\u003C/databasechangelog>\n",[18,5166,5167],{},"When I run Liquibase via command line, it sets up the „Person“ table. The relevant command is „update“:",[42,5169,5171],{"className":3419,"code":5170,"language":3421,"meta":47,"style":47},"\n./liquibase --url=jdbc:mysql://localhost:3306/liquiblog --driver=com.mysql.jdbc.Driver --username=root --password=\"\" --changeLogFile=db.changelog-0.1.0.xml \u003Cb>update\u003C/b>\n\n",[22,5172,5173,5177],{"__ignoreMap":47},[51,5174,5175],{"class":53,"line":54},[51,5176,304],{"emptyLinePlaceholder":303},[51,5178,5179],{"class":53,"line":69},[51,5180,5181],{},"./liquibase --url=jdbc:mysql://localhost:3306/liquiblog --driver=com.mysql.jdbc.Driver --username=root --password=\"\" --changeLogFile=db.changelog-0.1.0.xml \u003Cb>update\u003C/b>\n",[18,5183,5184],{},"Liquibase already knows how to roll back certain changesets, like the „createTable“ changeset above. If we call the\ncommand line client with „rollbackCount 1“ instead of „update“, it rolls back the last changeset it executed, and the\n„Person“ table is gone.",[18,5186,5187],{},"Other changesets cannot be rolled back automatically. Consider the following „insert“-changeset that inserts an entry\ninto our „Person“ table:",[42,5189,5191],{"className":5088,"code":5190,"language":5090,"meta":47,"style":47},"\n\u003Cdatabasechangelog xmlns=\"http://www.liquibase.org/xml/ns/dbchangelog\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemalocation=\"http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-2.0.xsd\">\n \u003Cchangeset author=\"mueller@synyx.de\" id=\"init-1\">\n \u003Cinsert tablename=\"Person\">\n \u003Ccolumn name=\"name\" value=\"John Doe\">\n \u003C/column>\n \u003C/insert>\n \u003Crollback>\n DELETE FROM Person WHERE name LIKE 'John Doe';\n \u003C/rollback>\n \u003C/changeset>\n\u003C/databasechangelog>\n\n",[22,5192,5193,5197,5201,5206,5211,5216,5220,5225,5230,5235,5240,5244],{"__ignoreMap":47},[51,5194,5195],{"class":53,"line":54},[51,5196,304],{"emptyLinePlaceholder":303},[51,5198,5199],{"class":53,"line":69},[51,5200,5101],{},[51,5202,5203],{"class":53,"line":82},[51,5204,5205],{}," \u003Cchangeset author=\"mueller@synyx.de\" id=\"init-1\">\n",[51,5207,5208],{"class":53,"line":205},[51,5209,5210],{}," \u003Cinsert tablename=\"Person\">\n",[51,5212,5213],{"class":53,"line":241},[51,5214,5215],{}," \u003Ccolumn name=\"name\" value=\"John Doe\">\n",[51,5217,5218],{"class":53,"line":250},[51,5219,5131],{},[51,5221,5222],{"class":53,"line":256},[51,5223,5224],{}," \u003C/insert>\n",[51,5226,5227],{"class":53,"line":262},[51,5228,5229],{}," \u003Crollback>\n",[51,5231,5232],{"class":53,"line":272},[51,5233,5234],{}," DELETE FROM Person WHERE name LIKE 'John Doe';\n",[51,5236,5237],{"class":53,"line":282},[51,5238,5239],{}," \u003C/rollback>\n",[51,5241,5242],{"class":53,"line":294},[51,5243,5159],{},[51,5245,5246],{"class":53,"line":300},[51,5247,5164],{},[18,5249,5250],{},"I manually added a „rollback“-tag containg an SQL statement that reverses the changset. Note that the „rollback“-tag\ncan contain either SQL statements as text or certain Liquibase refactoring tags. Since we now have two change log xml\nfiles, I created a „master“-file that imports the other files in the order in which they should be executed:",[42,5252,5254],{"className":5088,"code":5253,"language":5090,"meta":47,"style":47},"\n\u003C?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\u003Cdatabasechangelog xmlns=\"http://www.liquibase.org/xml/ns/dbchangelog/1.9\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemalocation=\"http://www.liquibase.org/xml/ns/dbchangelog/1.9\n http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-1.9.xsd\">\n \u003Cinclude file=\"db.changelog-0.1.0.xml\">\u003C/include>\n \u003Cinclude file=\"db.changelog-0.1.0.init.xml\">\u003C/include>\n\u003C/databasechangelog>\n\n",[22,5255,5256,5260,5265,5270,5275,5280,5285],{"__ignoreMap":47},[51,5257,5258],{"class":53,"line":54},[51,5259,304],{"emptyLinePlaceholder":303},[51,5261,5262],{"class":53,"line":69},[51,5263,5264],{},"\u003C?xml version=\"1.0\" encoding=\"UTF-8\"?>\n",[51,5266,5267],{"class":53,"line":82},[51,5268,5269],{},"\u003Cdatabasechangelog xmlns=\"http://www.liquibase.org/xml/ns/dbchangelog/1.9\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemalocation=\"http://www.liquibase.org/xml/ns/dbchangelog/1.9\n",[51,5271,5272],{"class":53,"line":205},[51,5273,5274],{}," http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-1.9.xsd\">\n",[51,5276,5277],{"class":53,"line":241},[51,5278,5279],{}," \u003Cinclude file=\"db.changelog-0.1.0.xml\">\u003C/include>\n",[51,5281,5282],{"class":53,"line":250},[51,5283,5284],{}," \u003Cinclude file=\"db.changelog-0.1.0.init.xml\">\u003C/include>\n",[51,5286,5287],{"class":53,"line":256},[51,5288,5164],{},[18,5290,5291],{},"If we run the „update“ command with the master change log file, it checks wether the first changeset was already\nexecuted (depending on you rolled it back or not) and then executes the second changeset that adds a „Person“ entry. To\nmake this work, Liquibase create a helper table called „DATABASECHANGELOGS“ containg already-executed change sets along\nwith a hash value (to make sure no-one modifies changesets once they have been executed):",[42,5293,5295],{"className":3419,"code":5294,"language":3421,"meta":47,"style":47},"\nmysql> select id, md5sum, description from DATABASECHANGELOG;\n+--------+------------------------------------+--------------+\n| id | md5sum | description |\n+--------+------------------------------------+--------------+\n| 1 | 3:5a36f447e90b35c3802cb6fe16cb12a7 | Create Table |\n| init-1 | 3:43c29e0011ebfcfd9cfbbb8450179a41 | Insert Row |\n+--------+------------------------------------+--------------+\n2 rows in set (0.00 sec)\n\n",[22,5296,5297,5301,5306,5311,5316,5320,5325,5330,5334],{"__ignoreMap":47},[51,5298,5299],{"class":53,"line":54},[51,5300,304],{"emptyLinePlaceholder":303},[51,5302,5303],{"class":53,"line":69},[51,5304,5305],{},"mysql> select id, md5sum, description from DATABASECHANGELOG;\n",[51,5307,5308],{"class":53,"line":82},[51,5309,5310],{},"+--------+------------------------------------+--------------+\n",[51,5312,5313],{"class":53,"line":205},[51,5314,5315],{},"| id | md5sum | description |\n",[51,5317,5318],{"class":53,"line":241},[51,5319,5310],{},[51,5321,5322],{"class":53,"line":250},[51,5323,5324],{},"| 1 | 3:5a36f447e90b35c3802cb6fe16cb12a7 | Create Table |\n",[51,5326,5327],{"class":53,"line":256},[51,5328,5329],{},"| init-1 | 3:43c29e0011ebfcfd9cfbbb8450179a41 | Insert Row |\n",[51,5331,5332],{"class":53,"line":262},[51,5333,5310],{},[51,5335,5336],{"class":53,"line":272},[51,5337,5082],{},[18,5339,5340],{},"Now that we got the basics running, lets try something more challenging: an actual change to our schema that requires\nboth schema and data migration. Our „Person“ table currently has only a name column, and we decided that we want to\nsplit it up into a „firstname“ and a „lastname“ column.",[18,5342,5343],{},"Before beginning work, I have Liquibase „tag“ the database so that we can roll back to this tag later on:",[42,5345,5347],{"className":3419,"code":5346,"language":3421,"meta":47,"style":47},"\n./liquibase --url=jdbc:mysql://localhost:3306/liquiblog --driver=com.mysql.jdbc.Driver --username=root --password=\"\" --changeLogFile=changelog-master.xml \u003Cb>tag liquiblog_0_1_0\u003C/b>\n\n",[22,5348,5349,5353],{"__ignoreMap":47},[51,5350,5351],{"class":53,"line":54},[51,5352,304],{"emptyLinePlaceholder":303},[51,5354,5355],{"class":53,"line":69},[51,5356,5357],{},"./liquibase --url=jdbc:mysql://localhost:3306/liquiblog --driver=com.mysql.jdbc.Driver --username=root --password=\"\" --changeLogFile=changelog-master.xml \u003Cb>tag liquiblog_0_1_0\u003C/b>\n",[18,5359,5360],{},"I created a new change set that adds the two new columns:",[42,5362,5364],{"className":5088,"code":5363,"language":5090,"meta":47,"style":47},"\n\u003Cchangeset author=\"mueller@synyx.de\" id=\"1\" runonchange=\"true\">\n \u003Caddcolumn tablename=\"Person\">\n \u003Ccolumn name=\"firstname\" type=\"VARCHAR(255)\">\n \u003Cconstraints nullable=\"false\">\n \u003C/constraints>\n \u003C/column>\n \u003Ccolumn name=\"lastname\" type=\"VARCHAR(255)\">\n \u003Cconstraints nullable=\"false\">\n \u003C/constraints>\n \u003C/column>\n \u003C/addcolumn>\n\u003C/changeset>\n\n",[22,5365,5366,5370,5375,5380,5385,5390,5395,5400,5405,5409,5413,5417,5422],{"__ignoreMap":47},[51,5367,5368],{"class":53,"line":54},[51,5369,304],{"emptyLinePlaceholder":303},[51,5371,5372],{"class":53,"line":69},[51,5373,5374],{},"\u003Cchangeset author=\"mueller@synyx.de\" id=\"1\" runonchange=\"true\">\n",[51,5376,5377],{"class":53,"line":82},[51,5378,5379],{}," \u003Caddcolumn tablename=\"Person\">\n",[51,5381,5382],{"class":53,"line":205},[51,5383,5384],{}," \u003Ccolumn name=\"firstname\" type=\"VARCHAR(255)\">\n",[51,5386,5387],{"class":53,"line":241},[51,5388,5389],{}," \u003Cconstraints nullable=\"false\">\n",[51,5391,5392],{"class":53,"line":250},[51,5393,5394],{}," \u003C/constraints>\n",[51,5396,5397],{"class":53,"line":256},[51,5398,5399],{}," \u003C/column>\n",[51,5401,5402],{"class":53,"line":262},[51,5403,5404],{}," \u003Ccolumn name=\"lastname\" type=\"VARCHAR(255)\">\n",[51,5406,5407],{"class":53,"line":272},[51,5408,5389],{},[51,5410,5411],{"class":53,"line":282},[51,5412,5394],{},[51,5414,5415],{"class":53,"line":294},[51,5416,5399],{},[51,5418,5419],{"class":53,"line":300},[51,5420,5421],{}," \u003C/addcolumn>\n",[51,5423,5424],{"class":53,"line":307},[51,5425,5426],{},"\u003C/changeset>\n",[18,5428,5429],{},"Once again, Liquibase knows how to roll back this change set, so we can skip the rollback tag.",[18,5431,5432],{},"Now that the table has two additional columns, we must take care of migrating our existing data to the new schema before\ndeleting the old, now obsolete „name“ column. Since data manipulation is not supported out-of-the-box by Liquibase,\nwe have to use its „sql“ tag to include native SQL statements within a changeset.",[42,5434,5436],{"className":5088,"code":5435,"language":5090,"meta":47,"style":47},"\n\u003Cchangeset author=\"mueller@synyx.de\" id=\"2\">\n \u003Csql>\n UPDATE Person SET firstname = SUBSTRING_INDEX(name, ' ', 1);\n UPDATE Person SET lastname = SUBSTRING_INDEX(name, ' ', -1);\n \u003C/sql>\n \u003Crollback>\n UPDATE Person SET firstname = '';\n UPDATE Person SET lastname = '';\n \u003C/rollback>\n\u003C/changeset>\n\n",[22,5437,5438,5442,5447,5452,5457,5462,5467,5472,5477,5482,5487],{"__ignoreMap":47},[51,5439,5440],{"class":53,"line":54},[51,5441,304],{"emptyLinePlaceholder":303},[51,5443,5444],{"class":53,"line":69},[51,5445,5446],{},"\u003Cchangeset author=\"mueller@synyx.de\" id=\"2\">\n",[51,5448,5449],{"class":53,"line":82},[51,5450,5451],{}," \u003Csql>\n",[51,5453,5454],{"class":53,"line":205},[51,5455,5456],{}," UPDATE Person SET firstname = SUBSTRING_INDEX(name, ' ', 1);\n",[51,5458,5459],{"class":53,"line":241},[51,5460,5461],{}," UPDATE Person SET lastname = SUBSTRING_INDEX(name, ' ', -1);\n",[51,5463,5464],{"class":53,"line":250},[51,5465,5466],{}," \u003C/sql>\n",[51,5468,5469],{"class":53,"line":256},[51,5470,5471],{}," \u003Crollback>\n",[51,5473,5474],{"class":53,"line":262},[51,5475,5476],{}," UPDATE Person SET firstname = '';\n",[51,5478,5479],{"class":53,"line":272},[51,5480,5481],{}," UPDATE Person SET lastname = '';\n",[51,5483,5484],{"class":53,"line":282},[51,5485,5486],{}," \u003C/rollback>\n",[51,5488,5489],{"class":53,"line":294},[51,5490,5426],{},[18,5492,5493],{},"Note that the content of the „rollback“-tag is kind of redundant, but the tag itself is required because Liquibase\nprevents us from rolling back changesets that cannot be rolled back implicitly and have no explicit rollback tag.",[18,5495,5496],{},"Once again, after executing Liquibase with the „update“-option, the new changeset is run, and our newly-created\n„firstname“ and „lastname“ columns now contain data.",[18,5498,5499],{},"Finally, I want to remove the old „name“ column.",[42,5501,5503],{"className":5088,"code":5502,"language":5090,"meta":47,"style":47},"\n\u003Cchangeset author=\"mueller@synyx.de\" id=\"3\" runonchange=\"true\">\n \u003Cdropcolumn columnname=\"name\" tablename=\"Person\">\n \u003C/dropcolumn>\n \u003Crollback>\n \u003Caddcolumn tablename=\"Person\">\n \u003Ccolumn name=\"name\" type=\"VARCHAR(255)\">\n \u003Cconstraints nullable=\"false\">\n \u003C/constraints>\n \u003C/column>\n \u003C/addcolumn>\n \u003Csql>\n UPDATE Person SET name = CONCAT(firstname, CONCAT(' ', lastname));\n \u003C/sql>\n \u003C/rollback>\n\u003C/changeset>\n\n",[22,5504,5505,5509,5514,5519,5524,5528,5533,5537,5541,5545,5549,5554,5559,5564,5569,5573],{"__ignoreMap":47},[51,5506,5507],{"class":53,"line":54},[51,5508,304],{"emptyLinePlaceholder":303},[51,5510,5511],{"class":53,"line":69},[51,5512,5513],{},"\u003Cchangeset author=\"mueller@synyx.de\" id=\"3\" runonchange=\"true\">\n",[51,5515,5516],{"class":53,"line":82},[51,5517,5518],{}," \u003Cdropcolumn columnname=\"name\" tablename=\"Person\">\n",[51,5520,5521],{"class":53,"line":205},[51,5522,5523],{}," \u003C/dropcolumn>\n",[51,5525,5526],{"class":53,"line":241},[51,5527,5471],{},[51,5529,5530],{"class":53,"line":250},[51,5531,5532],{}," \u003Caddcolumn tablename=\"Person\">\n",[51,5534,5535],{"class":53,"line":256},[51,5536,5136],{},[51,5538,5539],{"class":53,"line":262},[51,5540,5141],{},[51,5542,5543],{"class":53,"line":272},[51,5544,5126],{},[51,5546,5547],{"class":53,"line":282},[51,5548,5131],{},[51,5550,5551],{"class":53,"line":294},[51,5552,5553],{}," \u003C/addcolumn>\n",[51,5555,5556],{"class":53,"line":300},[51,5557,5558],{}," \u003Csql>\n",[51,5560,5561],{"class":53,"line":307},[51,5562,5563],{}," UPDATE Person SET name = CONCAT(firstname, CONCAT(' ', lastname));\n",[51,5565,5566],{"class":53,"line":355},[51,5567,5568],{}," \u003C/sql>\n",[51,5570,5571],{"class":53,"line":392},[51,5572,5486],{},[51,5574,5575],{"class":53,"line":400},[51,5576,5426],{},[18,5578,5579],{},"Again, the changeset itself is quite simple because Liquibase supports dropping columns, but the „rollback“-tag is more\ncomplicated: I first re-add the old „name“-column using the standart „addColumn“-tag, and then I used a custom SQL\nstatement to set the columns value.",[18,5581,5582],{},"We end up with a new database schema, complete with data:",[42,5584,5586],{"className":3419,"code":5585,"language":3421,"meta":47,"style":47},"\nmysql> select * from Person;\n+----+-----------+------------+\n| id | firstname | lastname |\n+----+-----------+------------+\n| 1 | John | Doe |\n+----+-----------+------------+\n1 rows in set (0.00 sec)\n\n",[22,5587,5588,5592,5597,5602,5607,5611,5616,5620],{"__ignoreMap":47},[51,5589,5590],{"class":53,"line":54},[51,5591,304],{"emptyLinePlaceholder":303},[51,5593,5594],{"class":53,"line":69},[51,5595,5596],{},"mysql> select * from Person;\n",[51,5598,5599],{"class":53,"line":82},[51,5600,5601],{},"+----+-----------+------------+\n",[51,5603,5604],{"class":53,"line":205},[51,5605,5606],{},"| id | firstname | lastname |\n",[51,5608,5609],{"class":53,"line":241},[51,5610,5601],{},[51,5612,5613],{"class":53,"line":250},[51,5614,5615],{},"| 1 | John | Doe |\n",[51,5617,5618],{"class":53,"line":256},[51,5619,5601],{},[51,5621,5622],{"class":53,"line":262},[51,5623,5624],{},"1 rows in set (0.00 sec)\n",[18,5626,5627],{},"Because we created a tag earlier and included rollback instructions in all our changesets, we can always roll back these\nmodifications without loosing any data! By running..",[42,5629,5631],{"className":3419,"code":5630,"language":3421,"meta":47,"style":47},"\n./liquibase --url=jdbc:mysql://localhost:3306/liquiblog --driver=com.mysql.jdbc.Driver --username=root --password=\"\" --changeLogFile=changelog-master.xml \u003Cb>rollback liquiblog_0_1_0\u003C/b>\n\n",[22,5632,5633,5637],{"__ignoreMap":47},[51,5634,5635],{"class":53,"line":54},[51,5636,304],{"emptyLinePlaceholder":303},[51,5638,5639],{"class":53,"line":69},[51,5640,5641],{},"./liquibase --url=jdbc:mysql://localhost:3306/liquiblog --driver=com.mysql.jdbc.Driver --username=root --password=\"\" --changeLogFile=changelog-master.xml \u003Cb>rollback liquiblog_0_1_0\u003C/b>\n",[18,5643,5644],{},"..we get our original database back!",[18,5646,5647],{},"Of course, the example with splitting / concatenating strings is a little far-fetched, but the same principles can be\napplied to more sophisticated refactorings. I came across the idea for this blog post when we had to split an existing\ndomain class (mapped to a single table) into an abstract base class and two subclasses, preferrably without losing data.",[799,5649,3710],{},{"title":47,"searchDepth":69,"depth":69,"links":5651},[],[810],"2012-08-03T10:27:51","Recently, we started integrating Liquibase as a database schema migration tool into most of my team’s projects, for both\\nnew from-scratch projects and already existing ones. Liquibase is great because it allows us to use an SCM tool like\\nGit to manage different revisions of an applications database schema – or more specifically, the changes required to\\nmigrate the database schema from one revision to another.","https://synyx.de/blog/migrating-data-with-liquibase/",{},"/blog/migrating-data-with-liquibase",{"title":5017,"description":5026},"blog/migrating-data-with-liquibase",[5661,5662,828,829],"database","liquibase","Recently, we started integrating Liquibase as a database schema migration tool into most of my team’s projects, for both new from-scratch projects and already existing ones. Liquibase is great because…","Vinp4k8Ts7cd7z-85P0-KPtpbF-5oJqchl7xudePQQI",{"id":5666,"title":5667,"author":5668,"body":5670,"category":6583,"date":6584,"description":6585,"extension":815,"link":6586,"meta":6587,"navigation":303,"path":6588,"seo":6589,"slug":5674,"stem":6590,"tags":6591,"teaser":6593,"__hash__":6594},"blog/blog/consolidating-development-environments-a-bash-magic-tutorial.md","Consolidating development environments – a Bash Magic tutorial",[5669],"jbuch",{"type":11,"value":5671,"toc":6581},[5672,5675,5678,5681,5684,5687,5707,5710,5720,5723,5726,5770,5773,5776,5961,5991,5996,6004,6009,6020,6023,6270,6273,6276,6512,6515,6519,6531,6535,6543,6546,6572,6575,6578],[14,5673,5667],{"id":5674},"consolidating-development-environments-a-bash-magic-tutorial",[18,5676,5677],{},"Developers have a tendency to not only work on a single project at once. Depending on those projects, there is a\nconstant struggle to keep your programming environment in sync with what you are actually doing. For that big legacy\nproduct you are maintaining you might need an old Java 1.5 in a specific version – for that fancy new web-app you\nmight be using the newest Java.",[18,5679,5680],{},"Different people have different strategies on how to take care of that problem. IDEs can be used to set software\nversions for specific projects, but using your build-tool from the commandline uses a completely different environment.\nAdapting your current command line environment is a little trickier and can solved in various ways – with varying\ncapabilities and problems.",[18,5682,5683],{},"This article is aimed at anyone wanting to learn a little more about Bash background magic – the resulting code will be\nworkable but will have to be fleshed out by the user to his own needs.",[18,5685,5686],{},"So, what do we actually want our “system” to do:",[903,5688,5689,5692,5695,5698,5701,5704],{},[906,5690,5691],{},"Control build environment (Java, Maven, …)",[906,5693,5694],{},"Special parameters for the build/execution environment (MAVEN_OPTS, …)",[906,5696,5697],{},"Switching environments while working",[906,5699,5700],{},"Switching environments based on project",[906,5702,5703],{},"Different environments in different terminals",[906,5705,5706],{},"More than one user should be able to reuse “environment profiles”",[18,5708,5709],{},"The first idea on how to switch an environment seems to be to create symlinks to the correct environment. That can be\npartly automated but isn’t really pretty and the environment is set for all terminals.",[18,5711,5712,5713,5719],{},"The way I will present here isn’t new, in fact ",[540,5714,5718],{"href":5715,"rel":5716,"title":5717},"http://rvm.io",[544],"rvm","http://rvm.io/"," probably does something very\nsimilar for Ruby (and is a lot more fleshed out most likely). But the technique is quite nice and makes switching\nenvironments quite unproblematic.",[18,5721,5722],{},"Having multiple users use the same kind of environment of course takes a little standardizing. The software for the\nbuild environment should be located at the same place for example. Our DevOps use Puppet to automate that, but that kind\nof automation isn’t really a requirement to use anything in this post.",[18,5724,5725],{},"Lets have a look how the heart of the technique looks like:",[42,5727,5729],{"className":44,"code":5728,"language":46,"meta":47,"style":47},"$ foo() { echo foo; }\n$ PROMPT_COMMAND=foo\nfoo\n$\n",[22,5730,5731,5753,5760,5765],{"__ignoreMap":47},[51,5732,5733,5736,5739,5742,5745,5748,5750],{"class":53,"line":54},[51,5734,5735],{"class":61},"$",[51,5737,5738],{"class":103}," foo",[51,5740,5741],{"class":65},"() ",[51,5743,5744],{"class":103},"{",[51,5746,5747],{"class":103}," echo",[51,5749,5738],{"class":103},[51,5751,5752],{"class":65},"; }\n",[51,5754,5755,5757],{"class":53,"line":69},[51,5756,5735],{"class":61},[51,5758,5759],{"class":103}," PROMPT_COMMAND=foo\n",[51,5761,5762],{"class":53,"line":82},[51,5763,5764],{"class":61},"foo\n",[51,5766,5767],{"class":53,"line":205},[51,5768,5769],{"class":61},"$\n",[18,5771,5772],{},"Bash lets you run a command before each prompt. The command is able to influence the current environment – this is\nimportant, as you want to set the profile for the current shell and don’t want to spawn subshells all the time. It can\nalso set how the next prompt will look like, using the PS1 environment variable.",[18,5774,5775],{},"Lets take that idea, put it in a file and build a little bit of code around it to build a quite basic profile switcher (\nwhich we will call profilehandler or ph for short).",[42,5777,5779],{"className":44,"code":5778,"language":46,"meta":47,"style":47},"profilehandler() {\n if [ x\"$PH_PROFILE\" = x\"new\" ]; then\n export JAVA_HOME=/opt/software/jdk/jdk1.7.0_04\n elif [ x\"$PH_PROFILE\" = x\"old\" ]; then\n export JAVA_HOME=/opt/software/jdk/jdk1.5.0_22\n fi\n if [ -n \"$PH_PROFILE\" ]; then\n export PATH=\"$JAVA_HOME/bin:$PATH\"\n fi\n export PS1=\"${PH_PROFILE:+[$PH_PROFILE] }\\$ \"\n}\nPROMPT_COMMAND=profilehandler\n",[22,5780,5781,5788,5815,5828,5852,5863,5868,5888,5911,5915,5947,5951],{"__ignoreMap":47},[51,5782,5783,5786],{"class":53,"line":54},[51,5784,5785],{"class":61},"profilehandler",[51,5787,1242],{"class":65},[51,5789,5790,5792,5795,5797,5800,5802,5804,5807,5810,5813],{"class":53,"line":69},[51,5791,688],{"class":57},[51,5793,5794],{"class":65}," [ x",[51,5796,343],{"class":103},[51,5798,5799],{"class":65},"$PH_PROFILE",[51,5801,343],{"class":103},[51,5803,2360],{"class":57},[51,5805,5806],{"class":65}," x",[51,5808,5809],{"class":103},"\"new\"",[51,5811,5812],{"class":65}," ]; ",[51,5814,389],{"class":57},[51,5816,5817,5820,5823,5825],{"class":53,"line":82},[51,5818,5819],{"class":57}," export",[51,5821,5822],{"class":65}," JAVA_HOME",[51,5824,75],{"class":57},[51,5826,5827],{"class":65},"/opt/software/jdk/jdk1.7.0_04\n",[51,5829,5830,5833,5835,5837,5839,5841,5843,5845,5848,5850],{"class":53,"line":205},[51,5831,5832],{"class":57}," elif",[51,5834,5794],{"class":65},[51,5836,343],{"class":103},[51,5838,5799],{"class":65},[51,5840,343],{"class":103},[51,5842,2360],{"class":57},[51,5844,5806],{"class":65},[51,5846,5847],{"class":103},"\"old\"",[51,5849,5812],{"class":65},[51,5851,389],{"class":57},[51,5853,5854,5856,5858,5860],{"class":53,"line":241},[51,5855,5819],{"class":57},[51,5857,5822],{"class":65},[51,5859,75],{"class":57},[51,5861,5862],{"class":65},"/opt/software/jdk/jdk1.5.0_22\n",[51,5864,5865],{"class":53,"line":250},[51,5866,5867],{"class":57}," fi\n",[51,5869,5870,5872,5875,5878,5880,5882,5884,5886],{"class":53,"line":256},[51,5871,688],{"class":57},[51,5873,5874],{"class":65}," [ ",[51,5876,5877],{"class":57},"-n",[51,5879,374],{"class":103},[51,5881,5799],{"class":65},[51,5883,343],{"class":103},[51,5885,5812],{"class":65},[51,5887,389],{"class":57},[51,5889,5890,5892,5895,5897,5899,5902,5905,5908],{"class":53,"line":262},[51,5891,5819],{"class":57},[51,5893,5894],{"class":65}," PATH",[51,5896,75],{"class":57},[51,5898,343],{"class":103},[51,5900,5901],{"class":65},"$JAVA_HOME",[51,5903,5904],{"class":103},"/bin:",[51,5906,5907],{"class":65},"$PATH",[51,5909,5910],{"class":103},"\"\n",[51,5912,5913],{"class":53,"line":272},[51,5914,5867],{"class":57},[51,5916,5917,5920,5923,5925,5928,5931,5933,5936,5938,5941,5944],{"class":53,"line":282},[51,5918,5919],{"class":57}," export",[51,5921,5922],{"class":65}," PS1",[51,5924,75],{"class":57},[51,5926,5927],{"class":103},"\"${",[51,5929,5930],{"class":65},"PH_PROFILE",[51,5932,3541],{"class":57},[51,5934,5935],{"class":103},"+[",[51,5937,5799],{"class":65},[51,5939,5940],{"class":103},"] }",[51,5942,5943],{"class":96},"\\$",[51,5945,5946],{"class":103}," \"\n",[51,5948,5949],{"class":53,"line":294},[51,5950,253],{"class":65},[51,5952,5953,5956,5958],{"class":53,"line":300},[51,5954,5955],{"class":65},"PROMPT_COMMAND",[51,5957,75],{"class":57},[51,5959,5960],{"class":103},"profilehandler\n",[42,5962,5964],{"className":44,"code":5963,"language":46,"meta":47,"style":47},"$ source profilehandler.sh\n$ PH_PROFILE=new\n[new] $ echo $JAVA_HOME/bin # -> 1.7\n",[22,5965,5966,5976,5983],{"__ignoreMap":47},[51,5967,5968,5970,5973],{"class":53,"line":54},[51,5969,5735],{"class":61},[51,5971,5972],{"class":103}," source",[51,5974,5975],{"class":103}," profilehandler.sh\n",[51,5977,5978,5980],{"class":53,"line":69},[51,5979,5735],{"class":61},[51,5981,5982],{"class":103}," PH_PROFILE=new\n",[51,5984,5985,5988],{"class":53,"line":82},[51,5986,5987],{"class":65},"[new] $ echo $JAVA_HOME/bin ",[51,5989,5990],{"class":1996},"# -> 1.7\n",[18,5992,5993],{},[1030,5994,5995],{},"Good:",[903,5997,5998,6001],{},[906,5999,6000],{},"Can set profile",[906,6002,6003],{},"Shows profile in path",[18,6005,6006],{},[1030,6007,6008],{},"Bad:",[903,6010,6011,6014,6017],{},[906,6012,6013],{},"Ugly interface",[906,6015,6016],{},"Leaks paths into PATH",[906,6018,6019],{},"Profiles are specified directly in code",[18,6021,6022],{},"Lets refactor that a little and make it slightly easier and safer to use.",[42,6024,6026],{"className":44,"code":6025,"language":46,"meta":47,"style":47},"# set up where our software/profiles will reside\nPH_HOME=$HOME/tmp/ph\nprofilehandler() {\n local profilepath\n # check if we have a valid profile\n if [ -f \"$PH_HOME/profiles/$PH_PROFILE\" ]; then\n profilepath=\"$PH_HOME/profiles/$PH_PROFILE\"\n fi\n # clean up\n local cleanpath=\"$(echo \"$PATH\" | sed 's#\\('$PH_HOME'[^:]*:\\)\\|\\(::\\)##g')\"\n export PATH=\"$cleanpath\"\n # a profile can be loaded, do it\n if [ -n \"$profilepath\" ]; then\n . \"$profilepath\"\n export PATH=\"${JAVA_HOME:+$JAVA_HOME/bin:}$PATH\"\n fi\n # set prompt\n export PS1=\"${PH_PROFILE:+[$PH_PROFILE] }\\$ \"\n}\n",[22,6027,6028,6033,6046,6052,6060,6065,6090,6107,6111,6116,6149,6164,6169,6188,6199,6233,6237,6242,6266],{"__ignoreMap":47},[51,6029,6030],{"class":53,"line":54},[51,6031,6032],{"class":1996},"# set up where our software/profiles will reside\n",[51,6034,6035,6038,6040,6043],{"class":53,"line":69},[51,6036,6037],{"class":65},"PH_HOME",[51,6039,75],{"class":57},[51,6041,6042],{"class":65},"$HOME",[51,6044,6045],{"class":103},"/tmp/ph\n",[51,6047,6048,6050],{"class":53,"line":82},[51,6049,5785],{"class":61},[51,6051,1242],{"class":65},[51,6053,6054,6057],{"class":53,"line":205},[51,6055,6056],{"class":57}," local",[51,6058,6059],{"class":65}," profilepath\n",[51,6061,6062],{"class":53,"line":241},[51,6063,6064],{"class":1996}," # check if we have a valid profile\n",[51,6066,6067,6069,6071,6074,6076,6079,6082,6084,6086,6088],{"class":53,"line":250},[51,6068,688],{"class":57},[51,6070,5874],{"class":65},[51,6072,6073],{"class":57},"-f",[51,6075,374],{"class":103},[51,6077,6078],{"class":65},"$PH_HOME",[51,6080,6081],{"class":103},"/profiles/",[51,6083,5799],{"class":65},[51,6085,343],{"class":103},[51,6087,5812],{"class":65},[51,6089,389],{"class":57},[51,6091,6092,6095,6097,6099,6101,6103,6105],{"class":53,"line":256},[51,6093,6094],{"class":65}," profilepath",[51,6096,75],{"class":57},[51,6098,343],{"class":103},[51,6100,6078],{"class":65},[51,6102,6081],{"class":103},[51,6104,5799],{"class":65},[51,6106,5910],{"class":103},[51,6108,6109],{"class":53,"line":262},[51,6110,5867],{"class":57},[51,6112,6113],{"class":53,"line":272},[51,6114,6115],{"class":1996}," # clean up\n",[51,6117,6118,6120,6123,6125,6128,6130,6132,6134,6137,6139,6141,6144,6146],{"class":53,"line":282},[51,6119,6056],{"class":57},[51,6121,6122],{"class":65}," cleanpath",[51,6124,75],{"class":57},[51,6126,6127],{"class":103},"\"$(",[51,6129,215],{"class":96},[51,6131,374],{"class":103},[51,6133,5907],{"class":65},[51,6135,6136],{"class":103},"\" ",[51,6138,221],{"class":57},[51,6140,233],{"class":61},[51,6142,6143],{"class":103}," 's#\\('",[51,6145,6078],{"class":65},[51,6147,6148],{"class":103},"'[^:]*:\\)\\|\\(::\\)##g')\"\n",[51,6150,6151,6153,6155,6157,6159,6162],{"class":53,"line":294},[51,6152,5919],{"class":57},[51,6154,5894],{"class":65},[51,6156,75],{"class":57},[51,6158,343],{"class":103},[51,6160,6161],{"class":65},"$cleanpath",[51,6163,5910],{"class":103},[51,6165,6166],{"class":53,"line":300},[51,6167,6168],{"class":1996}," # a profile can be loaded, do it\n",[51,6170,6171,6173,6175,6177,6179,6182,6184,6186],{"class":53,"line":307},[51,6172,688],{"class":57},[51,6174,5874],{"class":65},[51,6176,5877],{"class":57},[51,6178,374],{"class":103},[51,6180,6181],{"class":65},"$profilepath",[51,6183,343],{"class":103},[51,6185,5812],{"class":65},[51,6187,389],{"class":57},[51,6189,6190,6193,6195,6197],{"class":53,"line":355},[51,6191,6192],{"class":96}," .",[51,6194,374],{"class":103},[51,6196,6181],{"class":65},[51,6198,5910],{"class":103},[51,6200,6201,6203,6205,6207,6209,6212,6214,6217,6219,6222,6225,6227,6229,6231],{"class":53,"line":392},[51,6202,5819],{"class":57},[51,6204,5894],{"class":65},[51,6206,75],{"class":57},[51,6208,5927],{"class":103},[51,6210,6211],{"class":65},"JAVA_HOME",[51,6213,3541],{"class":57},[51,6215,6216],{"class":103},"+",[51,6218,5901],{"class":65},[51,6220,6221],{"class":57},"/",[51,6223,6224],{"class":65},"bin",[51,6226,3541],{"class":57},[51,6228,2556],{"class":103},[51,6230,5907],{"class":65},[51,6232,5910],{"class":103},[51,6234,6235],{"class":53,"line":400},[51,6236,5867],{"class":57},[51,6238,6239],{"class":53,"line":408},[51,6240,6241],{"class":1996}," # set prompt\n",[51,6243,6244,6246,6248,6250,6252,6254,6256,6258,6260,6262,6264],{"class":53,"line":439},[51,6245,5919],{"class":57},[51,6247,5922],{"class":65},[51,6249,75],{"class":57},[51,6251,5927],{"class":103},[51,6253,5930],{"class":65},[51,6255,3541],{"class":57},[51,6257,5935],{"class":103},[51,6259,5799],{"class":65},[51,6261,5940],{"class":103},[51,6263,5943],{"class":96},[51,6265,5946],{"class":103},[51,6267,6268],{"class":53,"line":448},[51,6269,253],{"class":65},[18,6271,6272],{},"Let the pain of that code dump subside a little bit, then proceed reading. We don’t do much more than the previous\nsnippet, just load the profile from an external file (in $PH_HOME/profiles) and make sure that the PATH gets cleaned up.\nThis method of clearing is quite fickle – you need to have all software in PH_HOME and certain edge cases do not get\nhandled – but for our simple requirements, that’s just fine.",[18,6274,6275],{},"Lets also create a nice UI so we can set our profile more elegantly.",[42,6277,6279],{"className":44,"code":6278,"language":46,"meta":47,"style":47},"# make a nice useable interface\nph() {\n case \"$1\" in\n set)\n if ! [ -f \"$PH_HOME/profiles/$2\" ]; then\n echo >&2 \"Bad profile: $2\"\n return\n fi\n export PH_PROFILE=\"$2\"\n ;;\n enable)\n ph set \"${2:-default}\"\n export oldPS1=\"$PS1\"\n export PROMPT_COMMAND=profilehandler\n ;;\n disable)\n unset PH_PROFILE\n unset PROMPT_COMMAND\n export PS1=\"$oldPS1\"\n ;;\n *)\n echo \"Usage: ph enable [profile]|disable|set profile\"\n esac\n}\n",[22,6280,6281,6286,6293,6308,6316,6342,6357,6362,6367,6383,6388,6395,6418,6434,6445,6449,6456,6464,6471,6486,6490,6495,6503,6508],{"__ignoreMap":47},[51,6282,6283],{"class":53,"line":54},[51,6284,6285],{"class":1996},"# make a nice useable interface\n",[51,6287,6288,6291],{"class":53,"line":69},[51,6289,6290],{"class":61},"ph",[51,6292,1242],{"class":65},[51,6294,6295,6298,6300,6303,6305],{"class":53,"line":82},[51,6296,6297],{"class":57}," case",[51,6299,374],{"class":103},[51,6301,6302],{"class":96},"$1",[51,6304,343],{"class":103},[51,6306,6307],{"class":57}," in\n",[51,6309,6310,6314],{"class":53,"line":205},[51,6311,6313],{"class":6312},"sA_wV"," set",[51,6315,202],{"class":57},[51,6317,6318,6320,6323,6325,6327,6329,6331,6333,6336,6338,6340],{"class":53,"line":241},[51,6319,358],{"class":57},[51,6321,6322],{"class":57}," !",[51,6324,5874],{"class":65},[51,6326,6073],{"class":57},[51,6328,374],{"class":103},[51,6330,6078],{"class":65},[51,6332,6081],{"class":103},[51,6334,6335],{"class":96},"$2",[51,6337,343],{"class":103},[51,6339,5812],{"class":65},[51,6341,389],{"class":57},[51,6343,6344,6347,6350,6353,6355],{"class":53,"line":250},[51,6345,6346],{"class":96}," echo",[51,6348,6349],{"class":57}," >&2",[51,6351,6352],{"class":103}," \"Bad profile: ",[51,6354,6335],{"class":96},[51,6356,5910],{"class":103},[51,6358,6359],{"class":53,"line":256},[51,6360,6361],{"class":57}," return\n",[51,6363,6364],{"class":53,"line":262},[51,6365,6366],{"class":57}," fi\n",[51,6368,6369,6372,6375,6377,6379,6381],{"class":53,"line":272},[51,6370,6371],{"class":57}," export",[51,6373,6374],{"class":65}," PH_PROFILE",[51,6376,75],{"class":57},[51,6378,343],{"class":103},[51,6380,6335],{"class":96},[51,6382,5910],{"class":103},[51,6384,6385],{"class":53,"line":282},[51,6386,6387],{"class":65}," ;;\n",[51,6389,6390,6393],{"class":53,"line":294},[51,6391,6392],{"class":6312}," enable",[51,6394,202],{"class":57},[51,6396,6397,6400,6403,6405,6408,6411,6414,6416],{"class":53,"line":300},[51,6398,6399],{"class":61}," ph",[51,6401,6402],{"class":103}," set",[51,6404,374],{"class":103},[51,6406,6407],{"class":96},"${2",[51,6409,6410],{"class":57},":-",[51,6412,6413],{"class":65},"default",[51,6415,2556],{"class":96},[51,6417,5910],{"class":103},[51,6419,6420,6422,6425,6427,6429,6432],{"class":53,"line":307},[51,6421,6371],{"class":57},[51,6423,6424],{"class":65}," oldPS1",[51,6426,75],{"class":57},[51,6428,343],{"class":103},[51,6430,6431],{"class":65},"$PS1",[51,6433,5910],{"class":103},[51,6435,6436,6438,6441,6443],{"class":53,"line":355},[51,6437,6371],{"class":57},[51,6439,6440],{"class":65}," PROMPT_COMMAND",[51,6442,75],{"class":57},[51,6444,5960],{"class":65},[51,6446,6447],{"class":53,"line":392},[51,6448,6387],{"class":65},[51,6450,6451,6454],{"class":53,"line":400},[51,6452,6453],{"class":6312}," disable",[51,6455,202],{"class":57},[51,6457,6458,6461],{"class":53,"line":408},[51,6459,6460],{"class":96}," unset",[51,6462,6463],{"class":103}," PH_PROFILE\n",[51,6465,6466,6468],{"class":53,"line":439},[51,6467,6460],{"class":96},[51,6469,6470],{"class":103}," PROMPT_COMMAND\n",[51,6472,6473,6475,6477,6479,6481,6484],{"class":53,"line":448},[51,6474,6371],{"class":57},[51,6476,5922],{"class":65},[51,6478,75],{"class":57},[51,6480,343],{"class":103},[51,6482,6483],{"class":65},"$oldPS1",[51,6485,5910],{"class":103},[51,6487,6488],{"class":53,"line":456},[51,6489,6387],{"class":65},[51,6491,6492],{"class":53,"line":465},[51,6493,6494],{"class":57}," *)\n",[51,6496,6497,6500],{"class":53,"line":471},[51,6498,6499],{"class":96}," echo",[51,6501,6502],{"class":103}," \"Usage: ph enable [profile]|disable|set profile\"\n",[51,6504,6505],{"class":53,"line":476},[51,6506,6507],{"class":57}," esac\n",[51,6509,6510],{"class":53,"line":481},[51,6511,253],{"class":65},[18,6513,6514],{},"We now have some sanity checking, a way to enable and disable our environment switcher and some amount of cleanup code\nso we get our old prompt back.",[18,6516,6517],{},[1030,6518,5995],{},[903,6520,6521,6523,6525,6528],{},[906,6522,6000],{},[906,6524,6003],{},[906,6526,6527],{},"Nicer interface",[906,6529,6530],{},"Profiles easily addable",[18,6532,6533],{},[1030,6534,6008],{},[903,6536,6537,6540],{},[906,6538,6539],{},"Insufficient cleanup (PATH retains Java path on disable)",[906,6541,6542],{},"Lacks features",[18,6544,6545],{},"But hey, that’s only our second take. And there is much more room for improvement.",[903,6547,6548,6551,6554,6557,6560,6563,6566,6569],{},[906,6549,6550],{},"Automatic profile switching on project directories (maybe implemented by a dotfile in the project directory – the\nprofilehandler can search down the stack for that and set the profile accordingly)",[906,6552,6553],{},"Local and global profile paths, so shared profiles can be distributed easily and users can override global settings",[906,6555,6556],{},"Configuring how the prompt looks",[906,6558,6559],{},"Showing the branch of your favorite SCM inside the prompt",[906,6561,6562],{},"Allow profiles to do their own cleanup/initialize",[906,6564,6565],{},"Support more than just Java",[906,6567,6568],{},"List available profiles",[906,6570,6571],{},"Much more exhaustive environment cleanup on disable",[18,6573,6574],{},"Having the capability to call a function before each prompt and in the scope of the current shell is very handy. But\nthat also means you have a limitation on how much you can do. Adding features is a good thing, but you have to take care\nto not extend the running time to a user-noticeable amount. You should make sure to only call programs which are sure\nto return in a short amount of time. If you are working partly on a network mounted disk, keep in mind that this could\npotentially block your shell for an extended amount of time. If you’re using automount and something (like looking up\nthe current GIT branch) in your profilehandler is looking at each directory up to the root, this also can hurt\nperformance. Also, while extending the profilehandler, if you get a stuck shell which does not show a prompt anymore,\nkeep an eye open for programs reading from stdin.",[18,6576,6577],{},"No matter which method you are using, it is probably a good idea to document your environment settings so everyone in\nthe project has a similar setup. And automating that process also sounds like a very good idea. So have fun playing\naround!",[799,6579,6580],{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sA_wV, html code.shiki .sA_wV{--shiki-default:#032F62;--shiki-dark:#DBEDFF}",{"title":47,"searchDepth":69,"depth":69,"links":6582},[],[809,810],"2012-07-23T12:47:58","Developers have a tendency to not only work on a single project at once. Depending on those projects, there is a\\nconstant struggle to keep your programming environment in sync with what you are actually doing. For that big legacy\\nproduct you are maintaining you might need an old Java 1.5 in a specific version – for that fancy new web-app you\\nmight be using the newest Java.","https://synyx.de/blog/consolidating-development-environments-a-bash-magic-tutorial/",{},"/blog/consolidating-development-environments-a-bash-magic-tutorial",{"title":5667,"description":5677},"blog/consolidating-development-environments-a-bash-magic-tutorial",[46,1570,184,6592,4631],"profiles","Developers have a tendency to not only work on a single project at once. Depending on those projects, there is a constant struggle to keep your programming environment in sync…","1DO3SrSal0CaXAOb3LB9CmmXX8wDLM5skOhuqLYUQd4",{"id":6596,"title":6597,"author":6598,"body":6600,"category":6701,"date":6702,"description":6703,"extension":815,"link":6704,"meta":6705,"navigation":303,"path":6706,"seo":6707,"slug":6604,"stem":6708,"tags":6709,"teaser":6712,"__hash__":6713},"blog/blog/my-take-on-things-java-community-events-vs-java-conferences.md","My take on things – Java Community events vs. Java Conferences",[6599],"arrasz",{"type":11,"value":6601,"toc":6699},[6602,6605,6608,6613,6616,6621,6626,6633,6636,6641,6646,6649,6652,6656,6661,6664,6667,6672,6675,6678,6683,6686,6689,6692,6696],[14,6603,6597],{"id":6604},"my-take-on-things-java-community-events-vs-java-conferences",[18,6606,6607],{},"Heute will ich einmal die Gelegenheit nutzen und die beiden wohl populärsten Konferenzen für Java-Entwicklung innerhalb\nEuropas gegenüberstellen. Dazu muss ich aber fairerweise anmerken, dass ich die Devoxx’2011 komplett besuchte, während\nich auf der JAX’2012 leider nur 2 Tage sein konnte.",[18,6609,6610],{},[1030,6611,6612],{},"Organisation:",[18,6614,6615],{},"Hier können beide Konferenzen eindeutig punkten. Die JAX war an den beiden Tagen meines Besuchs hervorragend\norganisiert, es gab kaum Wartezeiten oder aber überfüllte Säle, selbst bei den recht beliebten Keynotes nicht. Beide\nVeranstaltungen profitieren natürlich von den hervorragenden Locations. Dies macht die Organisation deutlich einfacher,\nwas man wirklich an vielen, vielen Kleinigkeiten bemerkt. Die Devoxx liegt hier aber aufgrund der Tatsache das sie in\neinem der größten Kinos in Europa stattfindet noch einen Punkt besser im Rennen. Die Bequemlichkeit der Säle ist einfach\nungeschlagen (Kinosessel der modernsten Art!) und natürlich auch die Sicht. Während man im einen oder anderen Talk auf\nder JAX auf einmal eine Säule oder den Beamer im Weg hatte (ist eben nicht besser machbar) gibt es aufgrund der\nperfekten Location auf der Devoxx solche Probleme nicht.",[18,6617,6618],{},[910,6619,6620],{},"+1 für die Devoxx",[18,6622,6623],{},[1030,6624,6625],{},"Verpflegung:",[18,6627,6628,6629,6632],{},"Die Verpflegung ist ja immer so eine Sache auf Konferenzen, die einen brauchen nur Kaffee, die anderen dauernd\nNervenfutter und Süßes für ihre ",[910,6630,6631],{},"Konzentration."," Die Devoxx bietet mit dauernden Kaffeetankstellen als auch dauernd\ngefüllten Kühlschränken recht ordentliche Ausstattung. Auch für Frühstück, Mittagessen und einen Nachmittagssnack ist\ngesorgt. An für sich gibt es hier keine Klagen. Ach doch, bitte, bitte liebe Devoxxianer … sorgt für besseren Kaffee!!!!\nIch bezahle gerne ein wenig mehr 🙂",[18,6634,6635],{},"Das Angebot der Devoxx ist allerdings in keinster Weise mit dem Angebot der JAX zu vergleichen. Auf der JAX gab es\nwährend der 2 Tage alles, was das Herz begehrt. Guten Kaffee, hervorragende Zwischensnacks und ein Buffet zu quasi jeder\nTageszeit, natürlich unterschiedlichst befüllt. Hierfür muss ich großen Respekt zollen. Das war einfach unfassbar gut\nund lecker! Einzig eine Kleinigkeit der Verbesserung fällt mir noch ein. Man könnte Beschildern, was wirklich\nvegetarisch oder aber vegan ist. Das Personal am Buffet konnte aber nahezu jede Frage beantworten und so hatte man auch\nals Vegetarier das gute Gefühl ordentlich zu essen 🙂",[18,6637,6638],{},[910,6639,6640],{},"+1 für die JAX",[18,6642,6643],{},[1030,6644,6645],{},"Technische Ausstattung:",[18,6647,6648],{},"Ja die technische Ausstattung ist natürlich so eine Sache. Die Devoxx ist hier klar im Vorteil durch die wirklich\ngeniale Location. Gegen solch einen Sound und eine Sicht und eine Lichtstärke der Beamer und Projektoren ist natürlich\nkein Gras gewachsen.",[18,6650,6651],{},"Vergleichbar sind also letztlich nur die kleineren Dinge wie Mikrofone oder aber die Gadgets. Auf beiden Seiten\narbeiteten aufopferungssvoll die Techniker an einer perfekten Situation. Ein kleiner Pluspunkt für die Devoxx vielleicht\naufgrund der sehr gut angenommenen Twitterwall und den einfach unschlagbar guten Apps für die Mobile Devices. Auf der\nDevoxx ist das WLan auch eindeutig besser, da man in einigen Sälen der JAX schlicht keines hatte (zumindest ging es mir\nund meinen Kollegen so für die Räume im Keller).",[18,6653,6654],{},[910,6655,6620],{},[18,6657,6658],{},[1030,6659,6660],{},"Anspruch Sessions, Workshops:",[18,6662,6663],{},"Hier erspare ich mir einen Vergleich, da es einfach kaum zu vergleichen ist. Die Devoxx wird seit 2 Jahren von den\ngroßen Firmen hinter Java als Podest genutzt und ist einfach auch besser international erreichbar, daher sind die\nSpeaker rein namentlich schon kaum zu schlagen (für den der Wert darauf legt). Oracle als auch Google oder Springsource\nhaben regelmäßig ihre TopSpeaker an Bord. Auf der JAX wirken die Talks auch deutlich lösungsorientierter, während es auf\nder Devoxx sehr sehr viel um die reine Entwicklung geht. Hier spürt man meiner Meinung nach einfach auch die Geschichte\nbeider Konferenzen noch sehr. Die Devoxx welche aus der Community entstand, während die JAX ein kommerzielles Event ist.",[18,6665,6666],{},"Daher erspare ich mir auch den Vergleich der Kosten, das macht reichlich wenig Sinn und jeder sollte für sich\nentscheiden, was hier ein gutes Maß ist und was nicht. Eine Kleinigkeit aber, die die Devoxxianer bereits seit einigen\nJahren machen, sollte sich hier die JAX doch noch abschauen: Den Talks ein Level mitgeben, für wen sie geeignet sind (\nbsp. Beginner, Senior, Expert oder vgl..) Ich saß dann doch ein zwei Mal in einem Talk, wo einfach das eigene Wissen\ngrößer war als das des Speakers.",[18,6668,6669],{},[1030,6670,6671],{},"Speaker:",[18,6673,6674],{},"Die Speaker sind natürlich eine sehr schwer vergleichbare Sache, alleine schon durch die oben bereits erwähnte Tatsache,\ndass die Devoxx von den großen drei als Bühne genutzt wird, während die JAX hier nicht so populär ist. Allerdings kann\nauch die JAX mit sehr namhaften und erfahrenen Speakern punkten und holt hier aus meiner Sicht Jahr für Jahr auf.",[18,6676,6677],{},"Ich mag hier keinen Punkt für die eine oder andere Seite geben.",[18,6679,6680],{},[1030,6681,6682],{},"Umgebung:",[18,6684,6685],{},"Die JAX findet in einer echten Konferenzlocation statt, während die Devoxx, wie bereits erwähnt in einem der größten\nKinokomplexe Europas stattfindet. Das macht sich natürlich auch in der Umgebung bemerkbar, könnte man meinen. Aber: Das\nKino ist wirklich derart am Ende von Antwerpen, dass dies viel eher ein Nachteil ist, denn ein Vorteil. Die\nRheingoldhalle in der die JAX nun seit 2011 stattfindet ist deutlich direkter in der Stadt selbst, was für das Programm\ndanach sehr von Vorteil ist.",[18,6687,6688],{},"Für mich als Besucher aus Süddeutschland ist die JAX natürlich eine Top-Location, da ich sogar pendeln kann, wenn ich\nmag. Die Devoxx schlägt hier direkt noch mit einer 5 Stunden Anfahrt zu, ist allerdings auch per Flieger direkt zu\nerreichen, während man für Mainz wohl am besten über FFM fliegt und dann noch ein wenig die DB genießt. Allerdings kann\nder Vorteil des direkten Flughafens in der Stadt wohl doch bei internationalen Speakern punkten.",[18,6690,6691],{},"Dennoch gebe ich aufgrund meiner persönlichen Situation hier den Punkt an die JAX.",[18,6693,6694],{},[910,6695,6640],{},[18,6697,6698],{},"Zusammenfassend kann man wohl sagen, dass beide Konferenzen ihre Fans haben und sich auch nicht so ähnlich sind, dass\nman nicht beide besuchen könnte. Ich persönlich werde wohl auch dieses Jahr wieder die Devoxx besuchen und dann im\nFrühjahr 2013 wieder die JAX… 😉",{"title":47,"searchDepth":69,"depth":69,"links":6700},[],[811],"2012-04-25T09:44:39","Heute will ich einmal die Gelegenheit nutzen und die beiden wohl populärsten Konferenzen für Java-Entwicklung innerhalb\\nEuropas gegenüberstellen. Dazu muss ich aber fairerweise anmerken, dass ich die Devoxx’2011 komplett besuchte, während\\nich auf der JAX’2012 leider nur 2 Tage sein konnte.","https://synyx.de/blog/my-take-on-things-java-community-events-vs-java-conferences/",{},"/blog/my-take-on-things-java-community-events-vs-java-conferences",{"title":6597,"description":6607},"blog/my-take-on-things-java-community-events-vs-java-conferences",[6710,1682,6711,1405,1685,828],"community","devoxx-conference","Heute will ich einmal die Gelegenheit nutzen und die beiden wohl populärsten Konferenzen für Java-Entwicklung innerhalb Europas gegenüberstellen. Dazu muss ich aber fairerweise anmerken, dass ich die Devoxx’2011 komplett besuchte,…","LlCT-W2p4EzOxwXO4tBpBQx5VG80g60BW-CYXAspPV0",{"id":6715,"title":6716,"author":6717,"body":6718,"category":7048,"date":7049,"description":7050,"extension":815,"link":7051,"meta":7052,"navigation":303,"path":7053,"seo":7054,"slug":6722,"stem":7055,"tags":7056,"teaser":7060,"__hash__":7061},"blog/blog/make-software-projects-fit-for-git.md","Make software-projects fit for git",[4544],{"type":11,"value":6719,"toc":7046},[6720,6723,6726,6732,6737,6761,6764,6770,6775,6785,6791,6797,6800,6834,6839,6842,6871,6874,6903,6906,6909,6984,6987,6990,6995,6998,7001,7004,7034,7037,7040,7043],[14,6721,6716],{"id":6722},"make-software-projects-fit-for-git",[18,6724,6725],{},"More and more Projects at our company are taking advantage of distributed and local revision control by using git. So to\nmake a complete software-project fit for git, by not only using git-svn with subversion and git on top, some more\nsteps are required than just handling files with git, learning its syntax and understanding the way it works…",[18,6727,6728],{},[960,6729],{"alt":6730,"src":6731},"\"git-logo\"","https://media.synyx.de/uploads//2011/10/git-logo.png",[18,6733,6734,1250],{},[910,6735,6736],{},"Source code has to be accessible",[18,6738,6739,6740,6746,6747,6753,6754,6760],{},"We are used to use subversion, a central repository with the leading stage of developement, when using git – all\nrepositories are equal. To take the best of both worlds we host a git-repository, which is defined to be leading (only\nby convention). We like to have things under control, so we\nuse ",[540,6741,6745],{"href":6742,"rel":6743,"title":6744},"http://eagain.net/gitweb/?p=gitosis.git",[544],"Gitosis download","gitosis"," to serve these repositories, but we think\nabout using ",[540,6748,6752],{"href":6749,"rel":6750,"title":6751},"https://github.com/sitaramc/gitolite/wiki",[544],"gitolite on GitHub","gitolite"," because of better/easier\naccess-management. You can also host at ",[540,6755,6759],{"href":6756,"rel":6757,"title":6758},"https://github.com/",[544],"Github","GitHub",", they do great work and it´s their daily\nbusiness.",[18,6762,6763],{},"What else do we need to develop an amazing piece of software in addition to good code and a working methodology? Which\ntools assist the development process and need capability to handle git-repositories?",[18,6765,6766],{},[960,6767],{"alt":6768,"src":6769},"\"chiliproject-logo\"","https://media.synyx.de/uploads//2011/10/chiliproject-logo.png",[18,6771,6772,1250],{},[910,6773,6774],{},"Software should do what it is intended for",[18,6776,6777,6778,6784],{},"To reach this goal we collect production requirements and fragment the subsequent work into processable packets with\na *\n*project-management tool** called redmine, more precisely ",[540,6779,6783],{"href":6780,"rel":6781,"title":6782},"https://www.chiliproject.org/",[544],"ChiliProject","**chiliproject\n**",", an rapidly evolving fork of redmine.",[18,6786,6787],{},[960,6788],{"alt":6789,"src":6790},"\"jenkins_logo\"","https://media.synyx.de/uploads//2011/10/jenkins_logo.png",[18,6792,6793,6796],{},[910,6794,6795],{},"Software is something executable",",",[18,6798,6799],{},"plain source-code is for documentation purposes 😉",[18,6801,6802,6803,6809,6810,6813,6814,6820,6821,6827,6828,2677],{},"We have to build it. Most of our projects are written in Java and built\nwith ",[540,6804,6808],{"href":6805,"rel":6806,"title":6807},"http://maven.apache.org/",[544],"Maven","Apache Maven",". To build the written code automatically and pursue the process of\n",[1030,6811,6812],{},"continuous integration"," we utilize a tool named hudson, more precisely ",[540,6815,6819],{"href":6816,"rel":6817,"title":6818},"http://jenkins-ci.org/",[544],"Jenkins","**Jenkins\n**",", an Oracle-independent fork (yeah we like using forks, especially when main\ndevelopers from the origin project are switching to the new fork, if you are interested in all of our reasons read\nthe ",[540,6822,6826],{"href":6823,"rel":6824,"title":6825},"http://blog.synyx.de/2011/05/opensource-is-not-just-about-the-license/",[544],"opensource is not just about the license","blogpost","\nwritten by ",[540,6829,6833],{"href":6830,"rel":6831,"title":6832},"http://blog.synyx.de/autoren/?uid=14",[544],"Fabian Buch","Fabian",[18,6835,6836],{},[1030,6837,6838],{},"So first mission is to make ChiliProject fit for git.",[18,6840,6841],{},"Luckily ChiliProject can handle git-repositories out of the box, but the repositories have to be cloned to localhost(\nthe machine running chiliproject). This can be achieved by giving read-rights to the user running chiliproject, in our\ncase this is generating a passwordless ssh-keypair, deploy the public part of it to gitosis and explicitly give rights\nto this public-key. To use the generated private-key every time we use git (in conjunction with ssh) we have to modify\nthe file ~/.ssh/config:",[42,6843,6845],{"className":44,"code":6844,"language":46,"meta":47,"style":47},"Host git.domain.tld\nUser git\nIdentityFile ~/.ssh/git_key.priv\n",[22,6846,6847,6855,6863],{"__ignoreMap":47},[51,6848,6849,6852],{"class":53,"line":54},[51,6850,6851],{"class":61},"Host",[51,6853,6854],{"class":103}," git.domain.tld\n",[51,6856,6857,6860],{"class":53,"line":69},[51,6858,6859],{"class":61},"User",[51,6861,6862],{"class":103}," git\n",[51,6864,6865,6868],{"class":53,"line":82},[51,6866,6867],{"class":61},"IdentityFile",[51,6869,6870],{"class":103}," ~/.ssh/git_key.priv\n",[18,6872,6873],{},"Now we need to clone the repository manually by login to the machine running Chili and do:",[42,6875,6877],{"className":44,"code":6876,"language":46,"meta":47,"style":47},"mkdir /path/to/git/repositories/\ncd /path/to/git/repositories/\ngit clone git@gitosis.domain.tld:gitrepo.git\n",[22,6878,6879,6887,6893],{"__ignoreMap":47},[51,6880,6881,6884],{"class":53,"line":54},[51,6882,6883],{"class":61},"mkdir",[51,6885,6886],{"class":103}," /path/to/git/repositories/\n",[51,6888,6889,6891],{"class":53,"line":69},[51,6890,823],{"class":96},[51,6892,6886],{"class":103},[51,6894,6895,6897,6900],{"class":53,"line":82},[51,6896,2102],{"class":61},[51,6898,6899],{"class":103}," clone",[51,6901,6902],{"class":103}," git@gitosis.domain.tld:gitrepo.git\n",[18,6904,6905],{},"for sure git has to be installed on the server running Chili. And the repository already exists…",[18,6907,6908],{},"but how do we keep this cloned repository up to date? We solved this by installing a cronjob running as the user,\nrunning Chili, every 5 minutes:",[42,6910,6912],{"className":44,"code":6911,"language":46,"meta":47,"style":47},"*/5 * * * * for i in /path/to/git/repositories/*/; do cd $i && git fetch; git reset refs/remotes/origin/master; done >>/dev/null 2>&1\n",[22,6913,6914],{"__ignoreMap":47},[51,6915,6916,6919,6922,6924,6927,6929,6931,6934,6937,6940,6943,6946,6949,6952,6955,6957,6960,6962,6964,6967,6970,6972,6975,6978,6981],{"class":53,"line":54},[51,6917,6918],{"class":57},"*",[51,6920,6921],{"class":65},"/5 ",[51,6923,6918],{"class":57},[51,6925,6926],{"class":57}," *",[51,6928,6926],{"class":57},[51,6930,6926],{"class":57},[51,6932,6933],{"class":57}," for",[51,6935,6936],{"class":65}," i ",[51,6938,6939],{"class":57},"in",[51,6941,6942],{"class":103}," /path/to/git/repositories/*/",[51,6944,6945],{"class":65},"; ",[51,6947,6948],{"class":57},"do",[51,6950,6951],{"class":96}," cd",[51,6953,6954],{"class":65}," $i && ",[51,6956,2102],{"class":61},[51,6958,6959],{"class":103}," fetch",[51,6961,6945],{"class":65},[51,6963,2102],{"class":61},[51,6965,6966],{"class":103}," reset",[51,6968,6969],{"class":103}," refs/remotes/origin/master",[51,6971,6945],{"class":65},[51,6973,6974],{"class":57},"done",[51,6976,6977],{"class":57}," >>",[51,6979,6980],{"class":65},"/dev/null ",[51,6982,6983],{"class":57},"2>&1\n",[18,6985,6986],{},"jap, you can log into a file instead of /dev/null, but we trust… 🙂",[18,6988,6989],{},"that´s it you can now add the local repository to your project in ChiliProject, but give full path including the\n“.git”-folder, it is little fussy in this point.",[18,6991,6992],{},[1030,6993,6994],{},"Get Jenkins to work with git",[18,6996,6997],{},"First, we have to do the same things done for ChiliProject, install the git-binary on the system running Jenkins,\ngenerate ssh-keypair, give read-rights to the user(possible stumbling block: we are running jenkins in a\njava-servlet-container so it´s the user running this container!)",[18,6999,7000],{},"modify ~/.ssh/config. Now we should be able to manually clone the targeted repositories, but that´s not what we want (\nremember automatically and continous integration?)",[18,7002,7003],{},"In order to be able to tag the built release version, the user running jenkins needs to give an author to the\ngit-repository, so modify/create ~/.gitconfig of this user:",[42,7005,7007],{"className":44,"code":7006,"language":46,"meta":47,"style":47},"[user]\n name = \"Jenkins\"\n email = \"jenkins@jenkins.domain.tld\"\n",[22,7008,7009,7014,7024],{"__ignoreMap":47},[51,7010,7011],{"class":53,"line":54},[51,7012,7013],{"class":65},"[user]\n",[51,7015,7016,7019,7021],{"class":53,"line":69},[51,7017,7018],{"class":61}," name",[51,7020,2360],{"class":103},[51,7022,7023],{"class":103}," \"Jenkins\"\n",[51,7025,7026,7029,7031],{"class":53,"line":82},[51,7027,7028],{"class":61}," email",[51,7030,2360],{"class":103},[51,7032,7033],{"class":103}," \"jenkins@jenkins.domain.tld\"\n",[18,7035,7036],{},"Jenkins is not able to handle git by default, we have to install a plugin: login -> Jenkins -> manage Jenkins ->\nmanage plugins -> available -> Git plugin (that´s easy to remember)",[18,7038,7039],{},"After restarting Jenkins you´ll find, under “projectX”/configure -> Source Code Management, the new section git where\nyou can insert the url of your repository -> save",[18,7041,7042],{},"Finally you can build the project(small prayer could help 😉 ) and enjoy the built software and Jenkins´ expandable\nworkflow…",[799,7044,7045],{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}",{"title":47,"searchDepth":69,"depth":69,"links":7047},[],[809,810,4258],"2011-10-25T12:34:45","More and more Projects at our company are taking advantage of distributed and local revision control by using git. So to\\nmake a complete software-project fit for git, by not only using git-svn with subversion and git on top, some more\\nsteps are required than just handling files with git, learning its syntax and understanding the way it works…","https://synyx.de/blog/make-software-projects-fit-for-git/",{},"/blog/make-software-projects-fit-for-git",{"title":6716,"description":6725},"blog/make-software-projects-fit-for-git",[7057,1405,7058,4922,7059,828],"apache","maven","project-management","More and more Projects at our company are taking advantage of distributed and local revision control by using git. So to make a complete software-project fit for git, by not…","vCNC4CoaSUI5ng_rt7Nu-7xypFBFlUaDbA6FV5gZLNA",{"id":7063,"title":7064,"author":7065,"body":7067,"category":7275,"date":7276,"description":7277,"extension":815,"link":7278,"meta":7279,"navigation":303,"path":7280,"seo":7281,"slug":7071,"stem":7283,"tags":7284,"teaser":7290,"__hash__":7291},"blog/blog/continuous-delivery-or-how-i-learned-to-stop-worrying-and-love-the-pipeline.md","Continuous Delivery or: How I Learned to Stop Worrying and Love the Pipeline",[7066],"speaker-schalanda",{"type":11,"value":7068,"toc":7273},[7069,7072,7113,7119,7122,7131,7134,7137,7160,7163,7170,7173,7201,7215,7224,7227,7243,7248,7254,7265],[14,7070,7064],{"id":7071},"continuous-delivery-or-how-i-learned-to-stop-worrying-and-love-the-pipeline",[18,7073,7074,7075,7078,7079,7084,7085,7090,7091,7096,7097,7102,7103,7096,7108,913],{},"Following our principle of ",[910,7076,7077],{},"Continuous Skill Enhancement"," here at ",[540,7080,7083],{"href":7081,"rel":7082},"https://synyx.de/leben-und-arbeiten/",[544],"Synyx"," I\nrecently read the\nbook ",[540,7086,7089],{"href":7087,"rel":7088},"http://www.informit.com/store/product.aspx?isbn=0321601912",[544],"Continuous Delivery: Reliable Software Releases through Build, Test, and Deployment Automation","\nby ",[540,7092,7095],{"href":7093,"rel":7094},"http://jezhumble.net/",[544],"Jez Humble"," (from ",[540,7098,7101],{"href":7099,"rel":7100},"http://www.thoughtworks.com/",[544],"ThoughtWorks",")\nand ",[540,7104,7107],{"href":7105,"rel":7106},"http://www.davefarley.net/",[544],"David Farley",[540,7109,7112],{"href":7110,"rel":7111},"http://www.lmaxtrader.co.uk/",[544],"LMAX",[18,7114,7115],{},[960,7116],{"alt":7117,"src":7118},"\"Continuous Delivery\"","https://media.synyx.de/uploads//2011/08/continuous_delivery_cover.jpeg",[18,7120,7121],{},"The book consists of three distinct parts.",[18,7123,7124,7125,7130],{},"Part one provides a high-level overview about the basics of software delivery. The authors are touching topics such as\nconfiguration management, continuous integration and software testing, describing what they are good for and what the\nchallenges are when implementing them. While the chapters help to understand the terminology used throughout the book\nthey don’t (and cannot) describe each of the topics in great detail – there\nare ",[540,7126,7129],{"href":7127,"rel":7128},"http://www.informit.com/store/product.aspx?isbn=0321336380",[544],"other books"," for that. But of course you’re already\nfamiliar with these topics so it’s just a little refresher.",[18,7132,7133],{},"Part two is dedicated to the central concept described in Continuous Delivery: the deployment pipeline. The idea is to\nreceive immediate feedback on errors and regressions as early in the development lifecycle of a project as possible and\nto provide a working application to the users as early and often as possible.",[18,7135,7136],{},"This means that every commit by a developer triggers a run of the deployment pipeline. It starts with building the\nartifact (obviously), proceeds to the first test stage running unit tests, from which it continues to the integration\ntest phase. If all tests ran successfully the artifact will continue through the stages of the deployment pipeline, e.\ng. a smoke test or non-functional test stage (think security and performance tests) and to a UAT (user acceptance\ntesting) stage. Finally the artifact will end up in the staging environment and from there it should require only the\nclick of a button to deploy it to production. Of course the authors describe each step in great detail and have some\nanecdotes from their projects to lighten up the text.",[18,7138,7139,7140,7145,7146,7149,7150,4312,7155,7159],{},"The central theme of part three is managing different parts of the delivery ecosystem. The authors discuss pros and cons\nof physical servers, virtualized servers and cloud computing, introduce the reader to the concepts of automatic machine\nprovisioning and configuration management with ",[540,7141,7144],{"href":7142,"rel":7143},"http://www.puppetlabs.com/",[544],"Puppet",", monitoring your systems and\ncollecting logs and performance data. They talk about managing test data, how to version it and how to get a basic stock\nof data for running integration tests in the first place. One chapter is dedicated to the challenges of managing\ncomponents and dependencies in which the authors discuss different strategies of versioning the components of your\napplication. It even comprises a short introduction to ",[540,7147,6808],{"href":6805,"rel":7148},[544],". In the following\nchapter the authors give an introduction to different revision control systems\nlike ",[540,7151,7154],{"href":7152,"rel":7153},"http://subversion.apache.org/",[544],"Subversion",[540,7156,1832],{"href":7157,"rel":7158},"http://gitscm.com/",[544],", as well as commercial alternatives like\nBitKeeper and ClearCase, and their respective advantages and disadvantages over the free alternatives. They continue to\ndescribe several advanced branching and integration strategies, each with its very own advantages and disadvantages in\ndifferent situations.",[18,7161,7162],{},"The last chapter swiftly copes with rather non-technical questions like the project lifecycle risk management and how\ncompliance and auditing are handled in a project using continuous delivery.",[18,7164,7165,7166,7169],{},"The concepts detailed in ",[910,7167,7168],{},"Continuous Delivery"," are not new per se but it’s the first book I read that really brought\nthese together in one coherent narrative. In fact, most of the concepts will seem to be obvious once you’ve read and\ngrokked them, but somehow nobody ever thought about them in depth.",[18,7171,7172],{},"Some of the distilled concepts are:",[903,7174,7175,7182,7185,7188,7195],{},[906,7176,7177,7178,7181],{},"Build binaries exactly ",[1030,7179,7180],{},"once",", store them in your artifact repository and promote them through the complete\ndeployment pipeline.",[906,7183,7184],{},"Only promote builds into staging or production that pass all unit and acceptance tests.",[906,7186,7187],{},"The development, testing, UAT and staging environments should be as similar as possible to the production environment.",[906,7189,7190,7191,7194],{},"Automate ",[1030,7192,7193],{},"everything",": builds, configuration, tests. Human interaction is prone to error, try to avoid it wherever\npossible.",[906,7196,7197,7198],{},"Use version control for ",[1030,7199,7200],{},"everything, including the configuration of underlying operating systems and infrastructure\nsuch as networking equipment.",[18,7202,7203,7205,7206,7211,7212,1250],{},[910,7204,7168],{}," has rightfully received much praise around the Internet and especially in the recently popularized\nDevOps movement. In 2011, the authors also won a ",[540,7207,7210],{"href":7208,"rel":7209},"http://drdobbs.com/joltawards/231500080?pgno=7",[544],"Jolt Excellence Award","\nin the category ",[910,7213,7214],{},"The Best Books",[18,7216,7217,7218,7223],{},"One thing I didn’t like about the book is the way online sources have been referenced in the text. Whenever the authors\nreference a website they provide an alphanumeric shortcode you know from URL shorteners like TinyURL. In fact that’s\nexactly what they are. These shortcodes can be used with Bit.ly or, as a fallback, directly from\nthe ",[540,7219,7222],{"href":7220,"rel":7221},"http://continuousdelivery.com/",[544],"supporting website"," of the book.",[18,7225,7226],{},"Example:",[903,7228,7229,7236],{},[906,7230,7231],{},[540,7232,7235],{"href":7233,"rel":7234},"http://bit.ly/bibNp0",[544],"bit.ly/bibNp0",[906,7237,7238],{},[540,7239,7242],{"href":7240,"rel":7241},"http://continuousdelivery.com/go/bibNp0",[544],"continuousdelivery.com/go/bibNp0",[18,7244,7245,7246,1250],{},"This often interrupts the flow of reading. Instead a more traditional style, e. g. placing the shortcodes in footnotes,\nwould have been preferable in the printed version of the book. I also missed a list of all referenced online sources,\neither at the end of each chapter or in a separate appendix. Fortunately this is really the only criticism I have for\n",[910,7247,7168],{},[18,7249,7250,7251,7253],{},"As a conclusion, I can really recommend reading ",[910,7252,7168],{}," to anyone involved in developing and delivering\nsoftware. It will provide some new points of view on your work and give you some new ideas about how to improve your\ncurrent processes. I, for one, am looking forward to applying the principles outlined in this book to some of our\nprojects.",[18,7255,7256,7257,140,7260,1250],{},"If you’re hooked now you might want to read the sample chapter from ",[910,7258,7259],{},"Continuous\nDelivery",[540,7261,7264],{"href":7262,"rel":7263},"http://www.informit.com/content/images/9780321601919/samplepages/0321601912.pdf",[544],"Chapter 5 – Anatomy of the Deployment Pipeline",[18,7266,7267,7268],{},"Oh, and by the way: ",[540,7269,7272],{"href":7270,"rel":7271},"https://jobs.synyx.de/",[544],"We’re hiring!",{"title":47,"searchDepth":69,"depth":69,"links":7274},[],[810],"2011-08-23T12:00:36","Following our principle of Continuous Skill Enhancement here at Synyx I\\nrecently read the\\nbook Continuous Delivery: Reliable Software Releases through Build, Test, and Deployment Automation\\nby Jez Humble (from ThoughtWorks)\\nand David Farley (from LMAX).","https://synyx.de/blog/continuous-delivery-or-how-i-learned-to-stop-worrying-and-love-the-pipeline/",{},"/blog/continuous-delivery-or-how-i-learned-to-stop-worrying-and-love-the-pipeline",{"title":7064,"description":7282},"Following our principle of Continuous Skill Enhancement here at Synyx I\nrecently read the\nbook Continuous Delivery: Reliable Software Releases through Build, Test, and Deployment Automation\nby Jez Humble (from ThoughtWorks)\nand David Farley (from LMAX).","blog/continuous-delivery-or-how-i-learned-to-stop-worrying-and-love-the-pipeline",[7285,7286,7287,7288,7289,828],"book-review","continuous-delivery","continuous-deployment","continuous-integration","processes","Following our principle of Continuous Skill Enhancement here at Synyx I recently read the book Continuous Delivery: Reliable Software Releases through Build, Test, and Deployment Automation by Jez Humble (from…","f_JR-CIvgZ2VCVSWsQjdTvAK1X1BsmKrdOPqauLk5lc",{"id":7293,"title":7294,"author":7295,"body":7297,"category":7741,"date":7742,"description":7304,"extension":815,"link":7743,"meta":7744,"navigation":303,"path":7745,"seo":7746,"slug":7747,"stem":7748,"tags":7749,"teaser":7758,"__hash__":7759},"blog/blog/utilizing-git-to-dive-into-huge-code-bases.md","Utilizing Git to dive into huge code bases – Git SVN Tips",[7296],"buch",{"type":11,"value":7298,"toc":7739},[7299,7302,7305,7308,7311,7325,7328,7331,7334,7337,7340,7343,7346,7349,7352,7355,7358,7361,7364,7367,7370,7373,7376,7390,7420,7423,7426,7429,7443,7446,7460,7463,7466,7492,7495,7538,7541,7544,7547,7566,7594,7597,7600,7603,7606,7630,7633,7647,7674,7677,7680,7683,7686,7709,7717,7731,7734,7737],[14,7300,7294],{"id":7301},"utilizing-git-to-dive-into-huge-code-bases-git-svn-tips",[18,7303,7304],{},"Unfortunately there are still projects not on dvsc like git. That’s especially true",[18,7306,7307],{},"for enterprise customers which are at least stuck on Subversion if not worse.",[18,7309,7310],{},"So the first thing I do on new projects I join:",[42,7312,7314],{"className":3419,"code":7313,"language":3421,"meta":47,"style":47},"\n git svn clone -s svn-url\n\n",[22,7315,7316,7320],{"__ignoreMap":47},[51,7317,7318],{"class":53,"line":54},[51,7319,304],{"emptyLinePlaceholder":303},[51,7321,7322],{"class":53,"line":69},[51,7323,7324],{}," git svn clone -s svn-url\n",[18,7326,7327],{},"Or installing Git if I have to work on customer provided machines. That’s even more",[18,7329,7330],{},"important than the rest of a development environment like an IDE.",[18,7332,7333],{},"From experience I find it especially useful to experiment with new code basis",[18,7335,7336],{},"utilizing Git. Grown and big projects aren’t easy to understand architecturally and",[18,7338,7339],{},"implementation wise without digging deep. With the help of Git you can jump right",[18,7341,7342],{},"in, without fear and without messing everything up or having too much unrevertable",[18,7344,7345],{},"local changes. Just commit early and often! By doing it locally, in experimental",[18,7347,7348],{},"branches you can try and learn. Before you publish something to a wider audience",[18,7350,7351],{},"(svn) you can reorder, cherrypick and change everything or parts of it. Git is my",[18,7353,7354],{},"tool of choice to get my hands dirty with legacy code (new one too of course).",[18,7356,7357],{},"Some useful tips on how I use Git-SVN:",[18,7359,7360],{},"SVN history is linear, so you can’t use branches and merge the usual git-way without",[18,7362,7363],{},"thinking.",[18,7365,7366],{},"What often happens to me is that I implement a new feature, do some refactorings on",[18,7368,7369],{},"my way etc and an urgant bug report comes along. But I commited on master, don’t",[18,7371,7372],{},"want to push it to SVN yet since it’s not finished yet and might not be stable. What",[18,7374,7375],{},"to do? git svn dcommit would push all my local master commits to svn. The solution:",[42,7377,7379],{"className":3419,"code":7378,"language":3421,"meta":47,"style":47},"\n git branch featureA\n\n",[22,7380,7381,7385],{"__ignoreMap":47},[51,7382,7383],{"class":53,"line":54},[51,7384,304],{"emptyLinePlaceholder":303},[51,7386,7387],{"class":53,"line":69},[51,7388,7389],{}," git branch featureA\n",[42,7391,7393],{"className":3419,"code":7392,"language":3421,"meta":47,"style":47},"\u003Ctt>\n |svn | master\n ---o---o---o---o---o---o---o---o---o---o\n | featureA\n\u003C/tt>\n",[22,7394,7395,7400,7405,7410,7415],{"__ignoreMap":47},[51,7396,7397],{"class":53,"line":54},[51,7398,7399],{},"\u003Ctt>\n",[51,7401,7402],{"class":53,"line":69},[51,7403,7404],{}," |svn | master\n",[51,7406,7407],{"class":53,"line":82},[51,7408,7409],{}," ---o---o---o---o---o---o---o---o---o---o\n",[51,7411,7412],{"class":53,"line":205},[51,7413,7414],{}," | featureA\n",[51,7416,7417],{"class":53,"line":241},[51,7418,7419],{},"\u003C/tt>\n",[18,7421,7422],{},"Now both branches featureA and master point to the latest commit. But we want master",[18,7424,7425],{},"to point to an earlier commit. Let’s say the last 10 commits aren’t in SVN yet and",[18,7427,7428],{},"the last 8 are experimental, so 2 could be pushed.",[42,7430,7432],{"className":3419,"code":7431,"language":3421,"meta":47,"style":47},"\n git reset --hard HEAD~8\n\n",[22,7433,7434,7438],{"__ignoreMap":47},[51,7435,7436],{"class":53,"line":54},[51,7437,304],{"emptyLinePlaceholder":303},[51,7439,7440],{"class":53,"line":69},[51,7441,7442],{}," git reset --hard HEAD~8\n",[18,7444,7445],{},"or",[42,7447,7449],{"className":3419,"code":7448,"language":3421,"meta":47,"style":47},"\n git reset --hard sha-hash-of-commit-to-point-to\n\n",[22,7450,7451,7455],{"__ignoreMap":47},[51,7452,7453],{"class":53,"line":54},[51,7454,304],{"emptyLinePlaceholder":303},[51,7456,7457],{"class":53,"line":69},[51,7458,7459],{}," git reset --hard sha-hash-of-commit-to-point-to\n",[18,7461,7462],{},"Now my master is in the state it was in 8 commits ago and my experimental changes",[18,7464,7465],{},"are still in featureA branch.",[42,7467,7469],{"className":3419,"code":7468,"language":3421,"meta":47,"style":47},"\u003Ctt>\n |svn | master\n ---o---o---o---o---o---o---o---o---o---o\n | featureA\n\u003C/tt>\n",[22,7470,7471,7475,7480,7484,7488],{"__ignoreMap":47},[51,7472,7473],{"class":53,"line":54},[51,7474,7399],{},[51,7476,7477],{"class":53,"line":69},[51,7478,7479],{}," |svn | master\n",[51,7481,7482],{"class":53,"line":82},[51,7483,7409],{},[51,7485,7486],{"class":53,"line":205},[51,7487,7414],{},[51,7489,7490],{"class":53,"line":241},[51,7491,7419],{},[18,7493,7494],{},"I can continue with fixing that critical bug, commit and svn dcommit. Have a look on how your history look with gitk\n–all.",[42,7496,7498],{"className":3419,"code":7497,"language":3421,"meta":47,"style":47},"\u003Ctt>\n | svn\n ---o---o---o master\n /\n ---o---o---o\n \\---o---o---o---o---o---o---o\n | featureA\n\u003C/tt>\n",[22,7499,7500,7504,7509,7514,7519,7524,7529,7534],{"__ignoreMap":47},[51,7501,7502],{"class":53,"line":54},[51,7503,7399],{},[51,7505,7506],{"class":53,"line":69},[51,7507,7508],{}," | svn\n",[51,7510,7511],{"class":53,"line":82},[51,7512,7513],{}," ---o---o---o master\n",[51,7515,7516],{"class":53,"line":205},[51,7517,7518],{}," /\n",[51,7520,7521],{"class":53,"line":241},[51,7522,7523],{}," ---o---o---o\n",[51,7525,7526],{"class":53,"line":250},[51,7527,7528],{}," \\---o---o---o---o---o---o---o\n",[51,7530,7531],{"class":53,"line":256},[51,7532,7533],{}," | featureA\n",[51,7535,7536],{"class":53,"line":262},[51,7537,7419],{},[18,7539,7540],{},"Dcommit rebased 3 commits and especially if",[18,7542,7543],{},"there were some more upstream svn commits, I want to base my experimental stuff",[18,7545,7546],{},"ontop of this. So I do a",[42,7548,7550],{"className":3419,"code":7549,"language":3421,"meta":47,"style":47},"\n git checkout featureA\n git rebase master\n\n",[22,7551,7552,7556,7561],{"__ignoreMap":47},[51,7553,7554],{"class":53,"line":54},[51,7555,304],{"emptyLinePlaceholder":303},[51,7557,7558],{"class":53,"line":69},[51,7559,7560],{}," git checkout featureA\n",[51,7562,7563],{"class":53,"line":82},[51,7564,7565],{}," git rebase master\n",[42,7567,7569],{"className":3419,"code":7568,"language":3421,"meta":47,"style":47},"\u003Ctt>\n | master\n ---o---o---o---o---o---o---o---o---o---o---o\n | svn | featureA\n\u003C/tt>\n",[22,7570,7571,7575,7580,7585,7590],{"__ignoreMap":47},[51,7572,7573],{"class":53,"line":54},[51,7574,7399],{},[51,7576,7577],{"class":53,"line":69},[51,7578,7579],{}," | master\n",[51,7581,7582],{"class":53,"line":82},[51,7583,7584],{}," ---o---o---o---o---o---o---o---o---o---o---o\n",[51,7586,7587],{"class":53,"line":205},[51,7588,7589],{}," | svn | featureA\n",[51,7591,7592],{"class":53,"line":241},[51,7593,7419],{},[18,7595,7596],{},"Even if I could live without the upstream changes on my featureA branch for now, I’d",[18,7598,7599],{},"need a rebase later anyway, so I can do it in advance. That’s because the history",[18,7601,7602],{},"wouldn’t be linear anymore by doing a three-way merge of my featureA into master without rebasing.",[18,7604,7605],{},"When I’m satisfied and with featureA and nothing changed in master I can",[42,7607,7609],{"className":3419,"code":7608,"language":3421,"meta":47,"style":47},"\n git checkout master\n git merge featureA\n git branch -d featureA\n\n",[22,7610,7611,7615,7620,7625],{"__ignoreMap":47},[51,7612,7613],{"class":53,"line":54},[51,7614,304],{"emptyLinePlaceholder":303},[51,7616,7617],{"class":53,"line":69},[51,7618,7619],{}," git checkout master\n",[51,7621,7622],{"class":53,"line":82},[51,7623,7624],{}," git merge featureA\n",[51,7626,7627],{"class":53,"line":205},[51,7628,7629],{}," git branch -d featureA\n",[18,7631,7632],{},"And since it’s a fast-forward merge can continue to push it to SVN",[42,7634,7636],{"className":3419,"code":7635,"language":3421,"meta":47,"style":47},"\n git svn dcommit\n\n",[22,7637,7638,7642],{"__ignoreMap":47},[51,7639,7640],{"class":53,"line":54},[51,7641,304],{"emptyLinePlaceholder":303},[51,7643,7644],{"class":53,"line":69},[51,7645,7646],{}," git svn dcommit\n",[42,7648,7650],{"className":3419,"code":7649,"language":3421,"meta":47,"style":47},"\u003Ctt>\n | master\n ---o---o---o---o---o---o---o---o---o---o---o\n | svn\n\u003C/tt>\n",[22,7651,7652,7656,7661,7665,7670],{"__ignoreMap":47},[51,7653,7654],{"class":53,"line":54},[51,7655,7399],{},[51,7657,7658],{"class":53,"line":69},[51,7659,7660],{}," | master\n",[51,7662,7663],{"class":53,"line":82},[51,7664,7584],{},[51,7666,7667],{"class":53,"line":205},[51,7668,7669],{}," | svn\n",[51,7671,7672],{"class":53,"line":241},[51,7673,7419],{},[18,7675,7676],{},"If something did change in master I just do another rebase before the merge.",[18,7678,7679],{},"If I come to the conclusion that my experimental branch was just for learning",[18,7681,7682],{},"purpose and only one or two useful refactoring or unit-test improving commits I take",[18,7684,7685],{},"only these to master and abandon the branch.",[42,7687,7689],{"className":3419,"code":7688,"language":3421,"meta":47,"style":47},"\n git checkout master\n git cherry-pick sha-of-one-commit\n git cherry-pick sha-of-another\n\n",[22,7690,7691,7695,7699,7704],{"__ignoreMap":47},[51,7692,7693],{"class":53,"line":54},[51,7694,304],{"emptyLinePlaceholder":303},[51,7696,7697],{"class":53,"line":69},[51,7698,7619],{},[51,7700,7701],{"class":53,"line":82},[51,7702,7703],{}," git cherry-pick sha-of-one-commit\n",[51,7705,7706],{"class":53,"line":205},[51,7707,7708],{}," git cherry-pick sha-of-another\n",[18,7710,7711,7712],{},"If I’m overall satisfied with the results of my experimental branch, but not with commit messages, how the commits are\nordered and maybe their scope, I use git\nrebases ",[540,7713,7716],{"href":7714,"rel":7715},"http://www.kernel.org/pub/software/scm/git/docs/git-rebase.html#_interactive_mode",[544],"interactive mode",[42,7718,7720],{"className":3419,"code":7719,"language":3421,"meta":47,"style":47},"\n git rebase -i sha-after-this-commit\n\n",[22,7721,7722,7726],{"__ignoreMap":47},[51,7723,7724],{"class":53,"line":54},[51,7725,304],{"emptyLinePlaceholder":303},[51,7727,7728],{"class":53,"line":69},[51,7729,7730],{}," git rebase -i sha-after-this-commit\n",[18,7732,7733],{},"reordering commits, splitting commits, editing commit messages, squashing multiple commits together.",[18,7735,7736],{},"There are endless more possibilities to get a better grip on your code-base.",[799,7738,3710],{},{"title":47,"searchDepth":69,"depth":69,"links":7740},[],[810],"2011-03-21T06:22:53","https://synyx.de/blog/utilizing-git-to-dive-into-huge-code-bases/",{},"/blog/utilizing-git-to-dive-into-huge-code-bases",{"title":7294,"description":7304},"utilizing-git-to-dive-into-huge-code-bases","blog/utilizing-git-to-dive-into-huge-code-bases",[7750,2102,7751,828,7752,7753,7754,7755,7756,7757],"dvcs","productivity","subversion","svn","tips","vcs","versioncontrol","withoutfear","Unfortunately there are still projects not on dvsc like git. That’s especially true for enterprise customers which are at least stuck on Subversion if not worse. So the first thing…","jY0wlPCOmzUfKbhiV6O6parFyGSU5NhgtbhpJnpQyIQ",{"id":7761,"title":7762,"author":7763,"body":7765,"category":7860,"date":7861,"description":7862,"extension":815,"link":7863,"meta":7864,"navigation":303,"path":7865,"seo":7866,"slug":7769,"stem":7868,"tags":7869,"teaser":7871,"__hash__":7872},"blog/blog/servlet-container-options-for-maven.md","Servlet container options for Maven",[7764],"hopf",{"type":11,"value":7766,"toc":7858},[7767,7770,7783,7792,7800,7809,7818,7821,7850,7853,7856],[14,7768,7762],{"id":7769},"servlet-container-options-for-maven",[18,7771,7772,7773,7776,7777,7782],{},"When developing web apps with ",[540,7774,6807],{"href":6805,"rel":7775},[544]," the de facto standard for running the app is to use the\nexcellent ",[540,7778,7781],{"href":7779,"rel":7780},"https://web.archive.org/web/20150520205353/https://docs.codehaus.org/display/JETTY/Maven+Jetty+Plugin",[544],"Maven Jetty Plugin","\nwhich runs the project in an embedded Jetty server. When configured, it can either run the project from the war file\ndirectly via mvn jetty:run or in exploded mode where the war is unpacked before being run (mvn jetty:run-exploded).\nThis noticably speeds up development as there is no need to manually deploy the artifact to a server.",[18,7784,7785,7786,7791],{},"But if the production system does not run on Jetty but on ",[540,7787,7790],{"href":7788,"rel":7789},"http://tomcat.apache.org/",[544],"Tomcat"," you might run into some\nproblems:",[903,7793,7794,7797],{},[906,7795,7796],{},"Some redirects from AJAX calls do work on Jetty but do not work on Tomcat",[906,7798,7799],{},"When submitting some forms on Jetty the parameters get lost",[18,7801,7802,7803,7808],{},"The latter can be noticed when running ",[540,7804,7807],{"href":7805,"rel":7806},"http://opencms.org",[544],"OpenCms"," on Jetty: Saving from the editor causes an error\nbecause the parameters are not available to OpenCms.",[18,7810,7811,7812,7817],{},"Fortunately there is a viable alternative:\nThe ",[540,7813,7816],{"href":7814,"rel":7815},"https://web.archive.org/web/20150531090420/http://mojo.codehaus.org/tomcat-maven-plugin/",[544],"Tomcat Maven Plugin",".\nBesides providing several options to deploy artifacts to an external server it can also be run in an embedded mode.",[18,7819,7820],{},"This is how the plugin is configured:",[42,7822,7824],{"className":5088,"code":7823,"language":5090,"meta":47,"style":47},"\n\u003Cplugin>\n \u003CgroupId>org.codehaus.mojo\u003C/groupId>\n \u003CartifactId>tomcat-maven-plugin\u003C/artifactId>\n\u003C/plugin>\n\n",[22,7825,7826,7830,7835,7840,7845],{"__ignoreMap":47},[51,7827,7828],{"class":53,"line":54},[51,7829,304],{"emptyLinePlaceholder":303},[51,7831,7832],{"class":53,"line":69},[51,7833,7834],{},"\u003Cplugin>\n",[51,7836,7837],{"class":53,"line":82},[51,7838,7839],{}," \u003CgroupId>org.codehaus.mojo\u003C/groupId>\n",[51,7841,7842],{"class":53,"line":205},[51,7843,7844],{}," \u003CartifactId>tomcat-maven-plugin\u003C/artifactId>\n",[51,7846,7847],{"class":53,"line":241},[51,7848,7849],{},"\u003C/plugin>\n",[18,7851,7852],{},"It can be run using mvn tomcat:run and mvn tomcat:run-war for running in unpacked war mode.",[18,7854,7855],{},"Using this plugin you can be sure that the features your servlet container provides in production are the same during\ndevelopment.",[799,7857,3710],{},{"title":47,"searchDepth":69,"depth":69,"links":7859},[],[810],"2010-07-09T08:48:36","When developing web apps with Maven the de facto standard for running the app is to use the\\nexcellent Maven Jetty Plugin\\nwhich runs the project in an embedded Jetty server. When configured, it can either run the project from the war file\\ndirectly via mvn jetty:run or in exploded mode where the war is unpacked before being run (mvn jetty:run-exploded).\\nThis noticably speeds up development as there is no need to manually deploy the artifact to a server.","https://synyx.de/blog/servlet-container-options-for-maven/",{},"/blog/servlet-container-options-for-maven",{"title":7762,"description":7867},"When developing web apps with Maven the de facto standard for running the app is to use the\nexcellent Maven Jetty Plugin\nwhich runs the project in an embedded Jetty server. When configured, it can either run the project from the war file\ndirectly via mvn jetty:run or in exploded mode where the war is unpacked before being run (mvn jetty:run-exploded).\nThis noticably speeds up development as there is no need to manually deploy the artifact to a server.","blog/servlet-container-options-for-maven",[1570,1405,7870,7058,4923],"jetty","When developing web apps with Maven the de facto standard for running the app is to use the excellent Maven Jetty Plugin which runs the project in an embedded Jetty…","fRYI1HRQAdsyYeA7rnRcKDEpC-Azc63aZtMg04FdDvU",{"id":7874,"title":7875,"author":7876,"body":7877,"category":7986,"date":7987,"description":7988,"extension":815,"link":7989,"meta":7990,"navigation":303,"path":7991,"seo":7992,"slug":7993,"stem":7994,"tags":7995,"teaser":7997,"__hash__":7998},"blog/blog/scrum-an-anti-word.md","Why is Scrum getting an anti-word?",[7296],{"type":11,"value":7878,"toc":7981},[7879,7882,7885,7888,7895,7900,7906,7909,7935,7938,7942,7945,7948,7951,7954,7957,7960,7963,7966,7969,7972,7975,7978],[14,7880,7875],{"id":7881},"why-is-scrum-getting-an-anti-word",[18,7883,7884],{},"For quite some years Scrum has been THE agile development process. Scrum got mainstream. But let’s have a look what got\nmainstream here. Scrum, Agility, Buzzwords, Scrum Master got mainstream as words, in business talk, in dev talk, in\ntrainings.",[18,7886,7887],{},"But what did it really achive for better communication, better relations and collaboration between developers, managers,\ncustomers etc. Has Scrum fundamentally improved the way software is delivered in our industry?",[18,7889,7890,7891,7894],{},"I probably couldn’t find many people who’d respond with an unconditional “",[1030,7892,7893],{},"YES!","” to this question.",[18,7896,7897],{},[1030,7898,7899],{},"But why? Why is Scrum getting an anti-word for many?",[18,7901,7902],{},[960,7903],{"alt":7904,"src":7905},"\"Security!\"","http://defunctscrum.blogspot.com/2007/07/should-you-leave-scrum-off-resume.html",[18,7907,7908],{},"There are various different reasons:",[903,7910,7911,7914,7917,7920,7923,7926,7929,7932],{},[906,7912,7913],{},"Scrum’s transparency creats angst for people living in and from intransparency",[906,7915,7916],{},"Scrum’s need for change is uncomfortable for people’s need for stability",[906,7918,7919],{},"Wrong implementation of Scrum",[906,7921,7922],{},"Scrum Master who don’t take people with them and run ahead in their own speed",[906,7924,7925],{},"Unbalanced power division between roles",[906,7927,7928],{},"Scrum Master who have THE solution instead of enabling teams to find differing ways for different problems",[906,7930,7931],{},"The Scrum hype and overwhelming/missleading marketing",[906,7933,7934],{},"Could go on with lots more",[18,7936,7937],{},"Let’s focus on a few and look at them a little bit more in-depth.",[1503,7939,7941],{"id":7940},"speedy-scrum-master","Speedy Scrum Master",[18,7943,7944],{},"A person who just got his Scrum Master Certificate and gets all enthusiastic about it returns to his company from\ntraining and wants to start. His textbook knowledge tells him how to technically implement Scrum, but without years of\nexperience it’s applied in a step-by-step way. Without soft-skills and knowing what’s appropriate when it’s allmost\nimpossible to be successful right away without watering down Scrum to non-Scrum. Picking people up where they are is\none of the key things. You can’t just tell them where they ought to be without telling why and what for.",[18,7946,7947],{},"Once this poor Scrum Master introduced Scrum to team for a few sprints he and his team will hit walls without knowing\nhow to climb over them. At this point the lack of experience of the Scrum Master leads to the first internal critics to\nsurface. The longer a Scrum Master only has his Scrum process goal in mind without providing real solutions the more\nScrum will be the scapegoat.",[18,7949,7950],{},"The more this happens (and I guess it does a lot), the more people will say “Oh no, not another of these Scrum guys”.",[1503,7952,7925],{"id":7953},"unbalanced-power-division-between-roles",[18,7955,7956],{},"Unbalanced power division between roles causes wrong implementation and frustration.",[18,7958,7959],{},"For example Scrum Masters who don’t have the support of upper management to make things happen.",[18,7961,7962],{},"Teams without the power to stop a sprint, without the needed skills and not cross-functional are handicapped teams.",[18,7964,7965],{},"Product Owner who are not directly responsible for the profit and loss or ROI of the product. I often wonder how a\nProduct Owner is supposed to prioritize without? I still often hear something along the lines “we need everything!”.",[18,7967,7968],{},"One of the things that’s done wrong in projects that use Scrum is that Scrum should help a project to fail early instead\nof staying for a long time and dying a long slow death over years. Why’s that? Because often all people involved have\nconflicting interests regarding their job safety and early project death. The only one that could have an interest in\nthat is a Product Owner with budget responsibility who knows that a project shouldn’t be continued if the value of to be\nimplemented stories is lower than its costs.",[1503,7970,7931],{"id":7971},"the-scrum-hype-and-overwhelmingmissleading-marketing",[18,7973,7974],{},"Scrum is often advertised as solution for everything. Scrum has been hyped for years. The result is lots of so called\n‘experts’ promote and implement Scrum without ensuring it’s done right or often even without the experience on how to do\nit right. Again Scrum gets the blame for failing projects that might or might not have failed anyway.",[18,7976,7977],{},"So is Scrum at fault? Or is it the way Scrum is used today? Can Scrum be rescued or do we need something new just\nbecause Scrum is done wrong instead of because it is wrong?",[18,7979,7980],{},"As with so many things I think we should focus more on quality instead of quantity. Don’t look for cookbook receipts,\nlet your team tailor their process inside the Scrum skeleton and share (don’t force it) your experience with them. I\nmight follow up on this in a future article.",{"title":47,"searchDepth":69,"depth":69,"links":7982},[7983,7984,7985],{"id":7940,"depth":82,"text":7941},{"id":7953,"depth":82,"text":7925},{"id":7971,"depth":82,"text":7931},[810],"2010-05-20T12:57:24","For quite some years Scrum has been THE agile development process. Scrum got mainstream. But let’s have a look what got\\nmainstream here. Scrum, Agility, Buzzwords, Scrum Master got mainstream as words, in business talk, in dev talk, in\\ntrainings.","https://synyx.de/blog/scrum-an-anti-word/",{},"/blog/scrum-an-anti-word",{"title":7875,"description":7884},"scrum-an-anti-word","blog/scrum-an-anti-word",[1799,7996,7289,3999,828],"anti-scrum","For quite some years Scrum has been THE agile development process. Scrum got mainstream. But let’s have a look what got mainstream here. Scrum, Agility, Buzzwords, Scrum Master got mainstream…","61KWdx6d7036gQMdieWCu_XLPod6Mrrx0FD70e-o9Jk",[8000,8003,8006,8009,8011,8014,8017,8020,8021,8024,8026,8029,8032,8035,8037,8040,8043,8046,8049,8052,8055,8058,8060,8063,8066,8069,8072,8074,8077,8080,8083,8086,8088,8091,8094,8097,8100,8103,8106,8109,8112,8115,8118,8121,8123,8126,8129,8132,8135,8137,8140,8143,8145,8148,8151,8153,8156,8159,8162,8165,8168,8171,8174,8177,8180,8183,8186,8189,8192,8195,8198,8201,8204,8207,8210,8213,8216,8219,8222,8225,8228,8231,8234,8237,8240,8243,8246,8249,8252,8254,8257,8260,8263,8266,8269,8272,8275,8278,8281,8283,8286,8288,8291,8294,8296,8298,8301,8304,8307,8310,8313,8315,8317,8319,8322,8325,8328,8331,8334,8337,8340,8343,8345,8348,8351,8354,8357,8360,8363,8366,8367,8370,8373,8376,8379,8382,8385,8388,8390,8393,8395,8398],{"slug":8001,"name":8002},"abel","Jennifer Abel",{"slug":8004,"name":8005},"allmendinger","Otto Allmendinger",{"slug":8007,"name":8008},"antony","Ben Antony",{"slug":6599,"name":8010},"Joachim Arrasz",{"slug":8012,"name":8013},"bauer","David Bauer",{"slug":8015,"name":8016},"bechtold","Janine Bechtold",{"slug":8018,"name":8019},"boersig","Jasmin Börsig",{"slug":7296,"name":6832},{"slug":8022,"name":8023},"buchloh","Aljona Buchloh",{"slug":1578,"name":8025},"Julia Burgard",{"slug":8027,"name":8028},"caspar-schwedes","Caspar Schwedes",{"slug":8030,"name":8031},"christina-schmitt","Christina Schmitt",{"slug":8033,"name":8034},"clausen","Michael Clausen",{"slug":1580,"name":8036},"Thomas Pötzsch",{"slug":8038,"name":8039},"damrath","Sebastian Damrath",{"slug":8041,"name":8042},"daniel","Markus Daniel",{"slug":8044,"name":8045},"dasch","Julia Dasch",{"slug":8047,"name":8048},"denman","Joffrey Denman",{"slug":8050,"name":8051},"dfuchs","Daniel Fuchs",{"slug":8053,"name":8054},"dobler","Max Dobler",{"slug":8056,"name":8057},"dobriakov","Vladimir Dobriakov",{"slug":8059,"name":8059},"dreiqbik",{"slug":8061,"name":8062},"dschaefer","Denise Schäfer",{"slug":8064,"name":8065},"dschneider","Dominik Schneider",{"slug":8067,"name":8068},"duerlich","Isabell Duerlich",{"slug":8070,"name":8071},"dutkowski","Bernd Dutkowski",{"slug":8073,"name":8073},"eifler",{"slug":8075,"name":8076},"essig","Tim Essig",{"slug":8078,"name":8079},"ferstl","Maximilian Ferstl",{"slug":8081,"name":8082},"fey","Prisca Fey",{"slug":8084,"name":8085},"frank","Leonard Frank",{"slug":1692,"name":8087},"Arnold Franke",{"slug":8089,"name":8090},"frischer","Nicolette Rudmann",{"slug":8092,"name":8093},"fuchs","Petra Fuchs",{"slug":8095,"name":8096},"gari","Sarah Gari",{"slug":8098,"name":8099},"gast","Gast",{"slug":8101,"name":8102},"graf","Johannes Graf",{"slug":8104,"name":8105},"grammlich","Daniela Grammlich",{"slug":8107,"name":8108},"guthardt","Sabrina Guthardt",{"slug":8110,"name":8111},"haeussler","Johannes Häussler",{"slug":8113,"name":8114},"hammann","Daniel Hammann",{"slug":8116,"name":8117},"heetel","Julian Heetel",{"slug":8119,"name":8120},"heft","Florian Heft",{"slug":1579,"name":8122},"Sebastian Heib",{"slug":8124,"name":8125},"heisler","Ida Heisler",{"slug":8127,"name":8128},"helm","Patrick Helm",{"slug":8130,"name":8131},"herbold","Michael Herbold",{"slug":8133,"name":8134},"hofmann","Peter Hofmann",{"slug":7764,"name":8136},"Florian Hopf",{"slug":8138,"name":8139},"jaud","Alina Jaud",{"slug":8141,"name":8142},"jayasinghe","Robin De Silva Jayasinghe",{"slug":5669,"name":8144},"Jonathan Buch",{"slug":8146,"name":8147},"junghanss","Gitta Junghanß",{"slug":8149,"name":8150},"kadyietska","Khrystyna Kadyietska",{"slug":2945,"name":8152},"Marc Kannegiesser",{"slug":8154,"name":8155},"karoly","Robert Károly",{"slug":8157,"name":8158},"karrasz","Katja Arrasz-Schepanski",{"slug":8160,"name":8161},"kaufmann","Florian Kaufmann",{"slug":8163,"name":8164},"kesler","Mike Kesler",{"slug":8166,"name":8167},"kirchgaessner","Bettina Kirchgäßner",{"slug":8169,"name":8170},"klem","Yannic Klem",{"slug":8172,"name":8173},"klenk","Timo Klenk",{"slug":8175,"name":8176},"knell","Tobias Knell",{"slug":8178,"name":8179},"knoll","Anna-Lena Knoll",{"slug":8181,"name":8182},"knorre","Matthias Knorre",{"slug":8184,"name":8185},"koenig","Melanie König",{"slug":8187,"name":8188},"kraft","Thomas Kraft",{"slug":8190,"name":8191},"krupicka","Florian Krupicka",{"slug":8193,"name":8194},"kuehn","Christian Kühn",{"slug":8196,"name":8197},"lange","Christian Lange",{"slug":8199,"name":8200},"larrasz","Luca Arrasz",{"slug":8202,"name":8203},"leist","Sascha Leist",{"slug":8205,"name":8206},"lihs","Michael Lihs",{"slug":8208,"name":8209},"linsin","David Linsin",{"slug":8211,"name":8212},"maniyar","Christian Maniyar",{"slug":8214,"name":8215},"martin","Björnie",{"slug":8217,"name":8218},"martin-koch","Martin Koch",{"slug":8220,"name":8221},"matt","Tobias Matt",{"slug":8223,"name":8224},"mennerich","Christian Mennerich",{"slug":8226,"name":8227},"menz","Alexander Menz",{"slug":8229,"name":8230},"meseck","Frederick Meseck",{"slug":8232,"name":8233},"messner","Oliver Messner",{"slug":8235,"name":8236},"michael-ploed","Michael Plöd",{"slug":8238,"name":8239},"mies","Marius Mies",{"slug":8241,"name":8242},"mihai","Alina Mihai",{"slug":8244,"name":8245},"moeller","Jörg Möller",{"slug":8247,"name":8248},"mohr","Rebecca Mohr",{"slug":8250,"name":8251},"moretti","David Moretti",{"slug":4006,"name":8253},"Sven Müller",{"slug":8255,"name":8256},"muessig","Alexander Müssig",{"slug":8258,"name":8259},"neupokoev","Grigory Neupokoev",{"slug":8261,"name":8262},"nussbaecher","Carmen Nussbächer",{"slug":8264,"name":8265},"ochs","Pascal Ochs",{"slug":8267,"name":8268},"oelhoff","Jan Oelhoff",{"slug":8270,"name":8271},"oengel","Yasin Öngel",{"slug":8273,"name":8274},"oezsoy","Enis Özsoy",{"slug":8276,"name":8277},"posch","Maya Posch",{"slug":8279,"name":8280},"ralfmueller","Ralf Müller",{"slug":8282,"name":8282},"redakteur",{"slug":8284,"name":8285},"reich","Michael Reich",{"slug":9,"name":8287},"Karl-Ludwig Reinhard",{"slug":8289,"name":8290},"rmueller","Rebecca Müller",{"slug":8292,"name":8293},"rosum","Jan Rosum",{"slug":8295,"name":8295},"rueckert",{"slug":4544,"name":8297},"Sascha Rüssel",{"slug":8299,"name":8300},"sauter","Moritz Sauter",{"slug":8302,"name":8303},"schaefer","Julian Schäfer",{"slug":8305,"name":8306},"scherer","Petra Scherer",{"slug":8308,"name":8309},"schlicht","Anne Schlicht",{"slug":8311,"name":8312},"schmidt","Jürgen Schmidt",{"slug":3736,"name":8314},"Tobias Schneider",{"slug":836,"name":8316},"Benjamin Seber",{"slug":4930,"name":8318},"Marc Sommer",{"slug":8320,"name":8321},"speaker-fels","Jakob Fels",{"slug":8323,"name":8324},"speaker-gierke","Oliver Gierke",{"slug":8326,"name":8327},"speaker-krupa","Malte Krupa",{"slug":8329,"name":8330},"speaker-mader","Jochen Mader",{"slug":8332,"name":8333},"speaker-meusel","Tim Meusel",{"slug":8335,"name":8336},"speaker-milke","Oliver Milke",{"slug":8338,"name":8339},"speaker-paluch","Mark Paluch",{"slug":8341,"name":8342},"speaker-schad","Jörg Schad",{"slug":7066,"name":8344},"Jochen Schalanda",{"slug":8346,"name":8347},"speaker-schauder","Jens Schauder",{"slug":8349,"name":8350},"speaker-unterstein","Johannes Unterstein",{"slug":8352,"name":8353},"speaker-wolff","Eberhard Wolff",{"slug":8355,"name":8356},"speaker-zoerner","Stefan Zörner",{"slug":8358,"name":8359},"stefan-belger","Stefan Belger",{"slug":8361,"name":8362},"steinegger","Roland Steinegger",{"slug":8364,"name":8365},"stern","sternchen synyx",{"slug":829,"name":829},{"slug":8368,"name":8369},"szulc","Mateusz Szulc",{"slug":8371,"name":8372},"tamara","Tamara Tunczinger",{"slug":8374,"name":8375},"theuer","Tobias Theuer",{"slug":8377,"name":8378},"thieme","Sandra Thieme",{"slug":8380,"name":8381},"thies-clasen","Marudor",{"slug":8383,"name":8384},"toernstroem","Olle Törnström",{"slug":8386,"name":8387},"ullinger","Max Ullinger",{"slug":3145,"name":8389},"Stephan Ulrich",{"slug":8391,"name":8392},"wagner","Stefan Wagner",{"slug":2769,"name":8394},"Andreas Weigel",{"slug":8396,"name":8397},"werner","Fabian Werner",{"slug":8399,"name":8400},"wolke","Sören Wolke",["Reactive",8402],{"$scookieConsent":8403,"$ssite-config":8405},{"functional":8404,"analytics":8404},false,{"_priority":8406,"env":8410,"name":8411,"url":8412},{"name":8407,"env":8408,"url":8409},-10,-15,0,"production","nuxt-app","https://synyx.de",["Set"],["ShallowReactive",8415],{"category-development":-1,"authors":-1},"/blog/tags/development"]