<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://www.daixtrose.de/feed.xml" rel="self" type="application/atom+xml" /><link href="https://www.daixtrose.de/" rel="alternate" type="text/html" /><updated>2025-10-17T16:39:06+00:00</updated><id>https://www.daixtrose.de/feed.xml</id><title type="html">Daixtrose Engineering</title><subtitle>Daixtrose Engineering by Markus Werle</subtitle><author><name>Markus Werle</name></author><entry><title type="html">The Bus Problem</title><link href="https://www.daixtrose.de/management/overcoming-the-bus-problem/" rel="alternate" type="text/html" title="The Bus Problem" /><published>2025-10-07T00:00:00+00:00</published><updated>2025-10-07T00:00:00+00:00</updated><id>https://www.daixtrose.de/management/overcoming-the-bus-problem</id><content type="html" xml:base="https://www.daixtrose.de/management/overcoming-the-bus-problem/"><![CDATA[<h2 id="disclaimer">Disclaimer</h2>

<div class="notice--warning">
<p>I cannot confirm nor deny that any of the events described in this post ever occurred. What I can say is that everything I wrote is true, even the parts I made up myself.</p>
</div>

<h2 id="as-pale-as-a-sheet">As Pale As A Sheet</h2>

<p>»As pale as a sheet, « I thought while watching my customers, the CEO and the project leader, who shifted nervously on their chairs. They were sitting to my left and right. Opposite me sat the leading developer of their only product. Totally unexpected, he had suddenly found the love of his life and decided to quit the company and relocate to a faraway place.</p>

<p>I was invited to interview the developer to determine what information was still missing during the handover to new employees who had recently joined the company and were not very pleased about facing an intensive phase of drinking from the firehose.</p>

<p>The meeting was scheduled for about an hour. It was clear that this was an emergency, so I decided to play it nice, not ask about software development processes, and did not put a <a href="https://www.isaca.org/search#q=cmmi&amp;first=16&amp;sort=relevancy&amp;f:@subjectsfacet=[CMMI]">CMMI</a> questionnaire on the table, but instead asked simple questions like, e.g.</p>

<ul>
  <li>Where do you store passwords? (“Nowhere, they are all in my head.”)</li>
  <li>How do you shut down and restart the production system (“That’s a 30-minute procedure where you manually generate and upload new container images to Azure, but we also run some VMs and some of them on a different hoster, for some, I am not sure whether we really need them.”)</li>
  <li>Is there an architecture diagram? (“No”)</li>
  <li>Is there a comprehensive list of all software components that are active in production and their corresponding repositories? (“No”)</li>
  <li>For the REST APIs in use, is there an OpenAPI specification? (“We deactivated autogeneration based on OpenApi files and did it manually.”)</li>
  <li>Is there any documentation about the REST APIs in use? (“There used to be a Word document, but it’s outdated. Read the code. It’s all there.”)    </li>
</ul>

<p>This went on for about three hours. »As pale as a sheet, « I thought while watching my customers, and rightly so. Luckily, I had taken notes on my laptop, approximately 15 pages of issues to be clarified.</p>

<p>It was apparent we had a huge bus problem. The <a href="https://en.wikipedia.org/wiki/Bus_factor">bus factor</a> is a measurement of the risk resulting from information and capabilities not being shared among team members, derived from the phrase “in case they get hit by a bus”.</p>

<p>Guessing from the color of my customers’ faces, I derived that I did not have to share the story of a good friend who was hit by a stroke that led to a total <a href="https://en.wikipedia.org/wiki/Aphasia">aphasia</a>. He eventually recovered and regained his speech, reaching a level where his struggle to find the words was only noticed by people who knew him before. The only data that never came back were all his passwords. My customer no longer needed to hear this story.      </p>

<p>On that day, the only thing I asked for was for the developer to make me an additional administrator of all systems (of <strong>all</strong> systems). Everyone was deeply exhausted, but I felt energetic to an extent that seemed inappropriate. So I kept that inside.</p>

<h2 id="a-deep-crisis-ahead">A Deep Crisis Ahead</h2>

<p>Having access to all repositories, my software archaeology project started on the same evening.</p>

<p>To add to the problem we were already facing, the code quality reflected the absence of any kind of product strategy, development processes, or application of best practices, let alone common sense.</p>

<p>The client informed me they were facing significant operational challenges. Over the years, their software stack had grown through an unstructured, ad-hoc approach, which was further exacerbated by high staff turnover. The result was a system plagued by errors and instability, leading to massive customer dissatisfaction and developer frustration.</p>

<p>From looking at the code, I could confirm they were in big trouble.</p>

<h2 id="nothing-to-lose">Nothing to Lose</h2>

<p>Having nothing more to lose gives you some freedom to take unorthodox measures. You can only win, because if you fail, no one can blame you, given the starting point where you joined.</p>

<p>I had been there before (create a working software solution in ten weeks for a piece of software that a team of five had failed to deliver within 3 years), but this time I perceived the task to be different. This was not a pure rescue action; it was more. I had the responsibility to create a genuinely sustainable solution, and for that, I had to develop a vision for how to enable the team. It turned out that the person who left was challenging to communicate with (surprise, surprise). Some team members had lost all hope, but to me, they appeared to be “<a href="https://en.wikipedia.org/wiki/Good_citizenship">good citizens</a>” who only needed someone to give them a little direction and hope.    </p>

<p>Since other developers had very recently joined, we had a considerable opportunity to form a new organization and set some things straight from the beginning.      </p>

<p>The next morning, I called the CEO and asked whether the current system was not in too much danger of failing immediately. He confirmed that the house of cards the company was built on showed some singularity in time and space and could run if (and only if) someone was restarting the whole system from time to time - as we had already learned, a tedious and manual process.</p>

<p>That was the point in our discussion where I requested a complete halt to all development activities. I needed the whole team to pursue my idea. The plan that I was going to propose was as follows:</p>

<ol>
  <li><strong>Collect all accounts and passwords</strong> and put them into a password database.</li>
  <li><strong>Document how the productive system could be shut down and restarted.</strong> The definition of done was something along the lines that a completion was reached when a person who had never heard about software development and had zero technical background could perform the task simply by reading the manual. We already had such a person on board: the CEO.    </li>
  <li>Completely <strong>document</strong> the source code <strong>repository structure</strong>, i.e., add a README to every repository that explains what the code was meant for, its maturity level, and whether it was actually part of the production system.</li>
  <li>Completely <strong>document</strong> the current system and <strong>all software components</strong>.</li>
  <li>Completely <strong>document</strong> the REST <strong>APIs in a machine-readable format</strong>, preferably using OpenApi v3 documents <strong>under version control</strong>.</li>
  <li><strong>Create</strong> some kind of <strong>architectural documents</strong></li>
  <li>Perform a deep source code analysis and make an <strong>improvement plan</strong>.    </li>
</ol>

<p>Luckily, I got a green light on the very same day.</p>

<h2 id="wiki-forever">Wiki Forever</h2>

<p>To enable the team, I provided clear direction on how to work collaboratively on the documentation.</p>

<p>After more than three decades in software development, I have reached the conclusion that the tools a company adopts not only reflect its culture but also actively shape it over time. I considered the choice of tools to be critical and was eager to find tooling that did not require a significant amount of education and was easy to use.</p>

<p>The first deed was to create a new git repository dedicated to holding all our documentation. I believed the accessibility and ease-of-use of the chosen documentation was crucial for our success, so I decided to go with a <a href="https://docs.github.com/en/communities/documenting-your-project-with-wikis/about-wikis">GitHub Wiki</a> inside a GitHub repository.</p>

<p>The plan was to put all files, such as API definition files, under version control and subject them to a review process via pull requests, and to utilize wiki pages for all regular documentation. The intuitive graphical user interface of the GitHub Wiki and the <a href="https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax">minimal set of markdown features</a> available for generating documentation helped to have everyone clearly instructed within a couple of hours.     </p>

<p>Because <a href="https://docs.github.com/en/communities/documenting-your-project-with-wikis/viewing-a-wikis-history-of-changes">wikis are Git repositories</a>, every change you make is a commit that you can (re-)view. Ultimately, the GitHub wiki is essentially a Git repository containing Markdown files. So, late at night, when no one was editing, I was able to pull the whole system onto my disk and perform some editorial work, like, e.g., cleanup, fix formatting errors, create consistency, reorganize documents, and push all changes back, so that everyone would find a clean environment the next morning.</p>

<p>Markdown has the advantage that it is easy to learn, allowing people to focus on content instead of wasting time on formatting issues. Based on the immediate feedback the team received from each other and from me, best practices for editing emerged within days.</p>

<h2 id="success-is-a-team-effort">Success is a Team Effort</h2>

<p>Over the following two weeks, we scheduled video conferences where the “bus problem” developer presented the entire system landscape and performed a live session for the shutdown and restart of the production system. The team then took the video recording, worked through it, and then tried hard while he was watching until they were able to do the same things. Then they put that information into the wiki.  </p>

<p>In the meantime, I had my own interview sessions with the individual and provided him with a list of information I would like to obtain. So he dug out some office documents from somewhere on his laptop (no backup) and shared them on a Google Drive. I collected the data and converted everything to new wiki pages using the Swiss Army Knife of document format conversions, <a href="https://pandoc.org">pandoc</a>.    </p>

<p>Initially, some team members were hesitant to use a wiki instead of a traditional text processor, but as the documentation evolved and they discovered how easy it was to correct errors, refine the text, and link to other sections, they became more comfortable with it.</p>

<p>Initially, it was I who, during meetings where people verbally shared new findings about the system, asked, “Have you put that piece of information in the wiki?” and persuaded them to share all their knowledge there, regardless of its importance. A couple of weeks later, the project manager called me and told me that I was no longer required to attend all knowledge exchange meetings: “You know what?” he said, “They start to ask each other exactly your question and they really mean it and they are really motivated and behind it all day long.”</p>

<p>I watched the wiki grow in size and quality during the following weeks.</p>

<p>After several internal tests with the project manager, they finally held a session where the CEO shared his screen, going through the Wiki document that explained how to shut down and restart the production system. It was a great success.  </p>

<p>My contribution to all this was a set of 70 action items for the next two years to further clean up the codebase and introduce proper Testing, Continuous Integration, and Continuous Deployment, as well as complete automation and code generation from interface specifications.</p>

<p>Together with a vast number of documentation tasks, we created tickets for each of them and assigned them to the appropriate team members. All was clearly visible in the cross-repository <a href="https://docs.github.com/en/issues/planning-and-tracking-with-projects/learning-about-projects/about-projects">Project</a> view, featuring a <a href="https://docs.github.com/en/issues/planning-and-tracking-with-projects/customizing-views-in-your-project/changing-the-layout-of-a-view#about-the-board-layout">Kanban board</a> which could be filtered for certain aspects and per assignee.</p>

<p>That was helpful for our daily standup. Yes, it came naturally to introduce agile best practices. I did not tell them it was Agile, though. Leave out the buzzwords, find out what works.</p>

<p>The next day, I got a call from my customer. They said</p>

<blockquote>
  <p>We always had a bad feeling about the whole system, but we couldn’t clearly identify what it was. We were also quite unhappy with the way our work was organized, as no one had an overview of what was happening within the team.</p>

  <p>You opened our eyes and gave us a <strong>clear vision</strong> of how to go on. And this <strong>lean project management tool is quite cool</strong>.</p>
</blockquote>

<h2 id="reflection">Reflection</h2>

<p>The person who left the company was actually quite intelligent. Yet, the work products he left behind were a real mess. I have questions about the overall system’s architecture. The code quality was not at the level it could have been.</p>

<p>So, what had happened here? What led to the mess? Why was there no structure? Why did they fail to organize themselves? And, since I am as German as German can be, the most important question is: Who is to <strong>blame</strong> for this? 😉    </p>

<p>Neil Kandalgaonkar <a href="https://xoxo.zone/@neilk/111485479147059557">once put it</a> this way<sup id="fnref:3" role="doc-noteref"><a href="#fn:3" class="footnote" rel="footnote">1</a></sup>:</p>

<blockquote>
  <p>Some people blame incompetent developers for cursed code. But the most cursed code is caused by highly competent developers. You just have to give them a task in a team that has the right kind of organizational dysfunctions - poor communication, power imbalances, diva designers or managers, unclear decisions. Where the incompetent developer would just fail, the highly competent developer will deliver an <strong>eldritch monstrosity</strong> - that works. Just don’t ask how it works.</p>
</blockquote>

<p>This statement is quite on the spot. In the case discussed here, I think that several factors contributed to the situation:</p>

<ol>
  <li>
    <p>A <strong>lack of true leadership</strong>:</p>

    <p>After all, it is always about the management. Management is to blame for everything. Really, I mean it. If managers fail to communicate purpose, prioritize, provide direction, and have an overview of what is happening within their own company, then they are doomed. Highly skilled developers can only partly compensate for a lack of true leadership.</p>

    <p>It is worth noting that the CEO was a person who was very reluctant to make decisions while everyone was still waiting for his advice and input.</p>
  </li>
  <li>
    <p>A <strong>lack of experience</strong>:    </p>

    <p>If you do not know how to set up a development process, if you have never learned about best practices for project management or for how to keep track of all tasks, then how are you going to survive facing a constant stream of requirements changes, incidents in the field, and trouble in the production system?</p>

    <p>It also becomes problematic when people who have never developed software and never engineered a system are the ones who decide how the work is organized. I disagree with those who think that good managers are not required to know the problem domain the engineering team is addressing.    </p>

    <p>For software, many good ideas on how to set up a development process emerged over the past decades. Except Scrum. Simply <a href="https://www.amazingcto.com/the-5-reasons-not-to-use-scrum/">do not use Scrum</a>. Using it only reveals your lack of experience<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">2</a></sup>. This is not the future you are looking for<sup id="fnref:2" role="doc-noteref"><a href="#fn:2" class="footnote" rel="footnote">3</a></sup>.  </p>
  </li>
  <li>
    <p>A <strong>lack of appropriate tooling</strong>:</p>

    <p>As outlined in <a href="/management/the-case-against-jira-part-2/">another post</a>, tools that generate more work by requiring significant administrative effort than they help reduce through their feature set can ruin your day. 
   
It is incredible to watch people come to the aha effect while using reductionist tools like the GitHub ticket system and its lean project views instead of a full-blown <a href="/management/the-case-against-jira-part-1/">JIRA/Confluence instance</a> or office suite. 
   
It always makes my day when people who used to drown in a never-ending stream of e-mail threads and a mass of documents on a shared drive finally learn that information can be shared in small text snippets under version control. I celebrate when they finally grasp it and fully understand the benefits of centralized knowledge management.      </p>
  </li>
</ol>

<h2 id="this-is-not-real">This is Not Real</h2>

<ul>
  <li>Are you sure?</li>
  <li>Do you make backups?</li>
  <li>Can you recover data from the backup you made two months ago?</li>
  <li>When was the last time you did this test?</li>
  <li>What happens if the data center gets flooded?</li>
  <li>Do you host a centralized password database with dedicated user access control and clear instructions about every password being stored exactly there?</li>
  <li>Do you have a backup of your centralized password database?</li>
  <li>What happens if the data center gets flooded? Do you still have a backup of your centralized password database?</li>
  <li>Did you make sure the password database backup is encrypted? Who holds the key to decrypting it? What happens when she gets hit by a bus?  </li>
  <li>How many different documentation systems do you run in parallel?</li>
  <li>Is your wiki up-to-date?</li>
  <li>Is every artifact the team has ever created documented?</li>
  <li>Do you have a centralized overview of who is working on what in your company and why?</li>
  <li>How large is the portion of maintenance effort that goes into the tools you use?  </li>
  <li>Do you have a development process?</li>
  <li>Do you think your development process is efficient?</li>
</ul>

<h2 id="meta-questions">Meta Questions</h2>

<ul>
  <li>Do you put <a href="https://agilemanifesto.org/">individuals and interactions over <strong>processes</strong> and <strong>tools</strong></a>?</li>
  <li>Do you put <a href="https://agilemanifesto.org/">working software over <strong>comprehensive documentation</strong></a></li>
</ul>

<h2 id="final-question">Final Question</h2>

<p>Would you like me to come to your company and ask some more questions about how you organize your work until you are <a href="#as-pale-as-a-sheet">as pale as a sheet</a>?  </p>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:3" role="doc-endnote">
      <p>Posted <a href="https://xoxo.zone/@neilk/111485479147059557">here</a> on Nov 28, 2023, 02:03 AM. <a href="#fnref:3" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:1" role="doc-endnote">
      <p>Take it with a grain of salt. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:2" role="doc-endnote">
      <p>TODO: add an animated GIF of a Jedi master waiving his hand.   <a href="#fnref:2" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>Markus Werle</name></author><category term="management" /><category term="JIRA" /><category term="GitHub" /><summary type="html"><![CDATA[Management is to blame for everything.]]></summary></entry><entry><title type="html">I am going to put music in your pocket</title><link href="https://www.daixtrose.de/product-design/I-am-going-to-put-music-in-your-pocket/" rel="alternate" type="text/html" title="I am going to put music in your pocket" /><published>2025-10-07T00:00:00+00:00</published><updated>2025-10-07T00:00:00+00:00</updated><id>https://www.daixtrose.de/product-design/I-am-going-to-put-music-in-your-pocket</id><content type="html" xml:base="https://www.daixtrose.de/product-design/I-am-going-to-put-music-in-your-pocket/"><![CDATA[<div class="notice--success">
<blockquote>
  <p>I am going to put music in your pocket […] somewhere between 500 and 1000 songs right in your pocket cuz I can’t stand looking at that inexplicable Walkman anymore. You’re carrying around a brick playing cassette tape. We’re not savages!</p>
</blockquote>
</div>

<h2 id="youve-got-to-start-with-the-customer-experience">You’ve got to start with the customer experience</h2>

<p>Steve Jobs once said the following <a href="https://youtu.be/oeqPrUmVz-o">wise words</a> about product design:</p>

<blockquote>
  <p><strong>You’ve got to start with the customer experience and work backwards to the technology.</strong></p>

  <p>You can’t start with the technology and try to figure out where you’re going to try to sell it and I’ve made this mistake probably more than anybody else in this room and I’ve got the scar tissue to prove it and I know that it’s the case and as we have tried to come up with a strategy and a vision for apple it started with “What incredible benefits can we give to the customer? Where can we take the customer?” not starting with “Let’s sit down with the engineers and figure out what awesome technology we have and then how we’re going to market that!” and I think that’s the right path to take.</p>
</blockquote>

<p>Through painful errors for which I had to pay high prices, I found out that this is easier said than done. You are always wiser after giving an idea a try<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup>.</p>

<h2 id="i-am-going-to-put-music-in-your-pocket">I am going to put music in your pocket</h2>

<p>Despite the specific dialogue being fictionalized for dramatic effect, and while this film scene in this exact form never actually happened, this movie scene from the movie “<a href="https://en.wikipedia.org/wiki/Steve_Jobs_(film)">Steve Jobs</a>” captures the emotional tension and key facts of Jobs’ relationship with his daughter Lisa:</p>

<!-- Courtesy of embedresponsively.com -->

<div class="responsive-video-container">
    <iframe src="https://www.youtube-nocookie.com/embed/nCdwDFDMkXQ" frameborder="0" webkitallowfullscreen="" mozallowfullscreen="" allowfullscreen=""></iframe>
  </div>

<p>At a critical point in the conversation, Steve Jobs finds no other way to express his love but by making a promise about key features of a product that had not even been worked on yet<sup id="fnref:8" role="doc-noteref"><a href="#fn:8" class="footnote" rel="footnote">2</a></sup>.</p>

<div>
<blockquote>
  <p>I am going to put music in your pocket […] somewhere between 500 and 1000 songs right in your pocket cuz I can’t stand looking at that inexplicable Walkman anymore. You’re carrying around a brick playing cassette tape. We’re not savages!</p>
</blockquote>
</div>

<h2 id="your-design-reflects-your-personality">Your design reflects your personality</h2>

<p>What strikes me about Steve Jobs is his profound impact on Apple’s products, even years after his death and more than ten additional generations of the iPhone. On the positive side, there is an astonishing number of key features of that product that confirm the customer experience drove the product design<sup id="fnref:4" role="doc-noteref"><a href="#fn:4" class="footnote" rel="footnote">3</a></sup>. In stark contrast to this, one can also observe that the much darker parts of Steve’s spirit are very much alive in those tiny boxes (and their larger tablet counterparts).</p>

<p><strong>And those are all about sharing</strong>.  </p>

<p>To say it nicely: Sharing was not Steve’s primary concern. Taking advantage of the help provided by others was<sup id="fnref:2" role="doc-noteref"><a href="#fn:2" class="footnote" rel="footnote">4</a></sup>.  This reflects in many facets of Apple’s products and ecosystem.  </p>

<p>Steve Jobs initially denied paternity for his daughter Lisa after her birth in 1978, even publicly rejecting that Lisa was his daughter and disputing the DNA test results. He even refused to provide financial support for Lisa and her mother, Chrisann Brennan. And he went beyond that. At one point, when Chrisann asked Jobs to buy a house for herself and their daughter, he toured the property she had in mind; however, he ultimately purchased the home for himself and his new wife, Laurene Powell Jobs<sup id="fnref:5" role="doc-noteref"><a href="#fn:5" class="footnote" rel="footnote">5</a></sup>.</p>

<p>Now, let’s examine Apple’s product landscape.</p>

<ul>
  <li>
    <p>You have that glossy iPhone or iPad at home? Well, iOS does not support multiple user profiles, making family sharing impossible for individuals who value privacy or data protection. Notice that the product design forces you to share all your data if you want to share your device. But why?</p>

    <p>Not exposing all your data to others, even within your own family, is a matter of responsibility. Your kids should not have insight into all of your communication, especially when a close friend sends a note about severe medical conditions (been there, seen that).  </p>
  </li>
  <li>
    <p>Ever tried to share that photo you made with your friend immediately? What if she does not possess an iPhone because she is a true professional and prefers the User Experience and configurability of an Android system? Hmm. The AirDrop protocol is proprietary. Even when reverse-engineered, it is not officially supported on Android devices, and Apple retains the power to restrict or modify these protocols at will, creating future barriers for interoperability.</p>

    <div class="image-container">
  <img src="/assets/images/webp/perplexity-generated-couple-eiffel-tower.webp" alt="A couple who cannot share a photo (AI generated)" />
  <div class="overlay">Cannot share your memories? (generated with Perplexity)</div>
</div>
  </li>
  <li>Observe how Apple<sup id="fnref:7" role="doc-noteref"><a href="#fn:7" class="footnote" rel="footnote">6</a></sup> fights back on any regulatory attempt to enforce interoperability among devices. We are lucky enough that they could not prevent the <a href="https://commission.europa.eu/news-and-media/news/eu-common-charger-rules-power-all-your-devices-single-charger-2024-12-28_en">EU common charger rules</a>.</li>
  <li>Despite the widespread use of open-source components in the underlying operating systems—such as the Mach kernel in iOS and macOS—Apple has an ambivalent attitude towards the Open Source idea. Andrew Hyatt<sup id="fnref:3" role="doc-noteref"><a href="#fn:3" class="footnote" rel="footnote">7</a></sup> put it this way:
 
    <blockquote>
      <p>Apple Open Source is truly a strange beast. In many ways, it portrays Apple as a company that values open source and wants to give back to the community. But in many ways, it falls short of this and represents an effort to <strong>look</strong> like they care about Free &amp; Open Source Software.  </p>
    </blockquote>
  </li>
</ul>

<p>Corporate personalities and their strategic choices can profoundly influence the characteristics of the products they create—shaping the user experience far beyond mere hardware and software design.</p>

<p>Let us acknowledge Steve Jobs’ remarkable achievements, while also not forgetting the challenges this world faces due to his shortcomings.</p>

<h2 id="its-all-about-me">It’s all about me</h2>

<p>And this is all about me. The most challenging question I have to ask myself is, to what extent my history, my brightness, and my darkness continue to influence the way I write software, the architecture I propose, how I shape products, how my way of thinking is reflected in the user interface and the user experience of all that I design.  </p>

<p>To give you one example: For what reason is the Ducktrain vehicle body made out of canted and riveted metal sheets? Was ist a good decision? At the time I made the decision, it appeared so. Of course, it had nothing to do with my fascination with airfoils since I was ten and having studied Aeronautics. 😉      </p>

<div class="image-container">
  <img src="/assets/images/webp/DSCF0849_DxO_cropped.webp" alt="Markus Werle in front of the Ducktrain prototype vehice" />
  <div class="overlay">Foto by <a href="https://jwm.media/">JWM Media</a></div>
</div>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:1" role="doc-endnote">
      <p>Please note that even when you intensively discuss your product with potential customers, there is plenty of room for failure. But this is <a href="/none/this-article-is-work-in-progress/">a story for another day</a>. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:8" role="doc-endnote">
      <p>Some people may dislike those salesmen who believe this is a healthy way to make progress. <a href="#fnref:8" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:4" role="doc-endnote">
      <p>I hope you noticed the formulation of the sentence in the past form. Apple is on a steep downward track regarding User Experience and Ergonomics. <a href="https://pluralistic.net/">Cory Doctorow</a> coined the term <a href="https://en.wikipedia.org/wiki/Enshittification">Enshittification</a> for this <a href="#fnref:4" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:2" role="doc-endnote">
      <p>Read <a href="https://www.inc.com/jeff-haden/28-years-ago-steve-jobs-said-asking-for-help-separates-those-who-do-from-those-who-dream-science-says-hes-still-right.html">this article</a>. The article describes asking for help as a positive thing, but astute readers recognize the one-way road the help takes in this context.   <a href="#fnref:2" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:5" role="doc-endnote">
      <p>To be fair, Steve Jobs eventually did provide some financial support over the years, including buying Chrisann two cars, paying for Lisa’s private school tuition, and purchasing a $400,000 house, though often begrudgingly and with minimal support relative to his wealth. <a href="#fnref:5" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:7" role="doc-endnote">
      <p>And Google, and Microsoft, and Adobe, and others. <a href="#fnref:7" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:3" role="doc-endnote">
      <p>Andrew Hyatt “<a href="https://ahyattdev.github.io/blog/2018/01/21/apple-open-source.html">Observations of Apple Open Source</a>”   <a href="#fnref:3" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>Markus Werle</name></author><category term="product-design" /><category term="Product Design" /><category term="Ergonomics" /><category term="Open Source" /><category term="User Experience" /><summary type="html"><![CDATA[Your design reflects your personality.]]></summary></entry><entry><title type="html">Converting Images to WebP with Copyright Metadata</title><link href="https://www.daixtrose.de/tutorial/image-processing/reducing-web-traffic-with-the-webp-format/" rel="alternate" type="text/html" title="Converting Images to WebP with Copyright Metadata" /><published>2025-10-02T00:00:00+00:00</published><updated>2025-10-02T00:00:00+00:00</updated><id>https://www.daixtrose.de/tutorial/image-processing/reducing-web-traffic-with-the-webp-format</id><content type="html" xml:base="https://www.daixtrose.de/tutorial/image-processing/reducing-web-traffic-with-the-webp-format/"><![CDATA[<h2 id="introduction">Introduction</h2>

<p>Modern web development demands optimized images that load quickly while maintaining visual quality. The WebP format, developed by Google, offers superior compression compared to traditional JPEG and PNG formats, typically reducing file sizes by 25-35% without noticeable quality loss. This guide walks through converting images to WebP format and embedding copyright metadata using command-line tools on Ubuntu Linux.</p>

<h2 id="prerequisites">Prerequisites</h2>

<p>This tutorial assumes you’re running Ubuntu 22.04 LTS or newer with the following tools installed:</p>

<ul>
  <li><strong>ImageMagick 6.9.11 or higher</strong> for image conversion</li>
  <li><strong>ExifTool 13.38 or higher</strong> for metadata management</li>
</ul>

<h2 id="step-1-converting-images-to-webp-format">Step 1: Converting Images to WebP Format</h2>

<p>The first step is converting your existing JPEG images to the WebP format using ImageMagick’s <code class="language-plaintext highlighter-rouge">convert</code> or <code class="language-plaintext highlighter-rouge">mogrify</code> commands.</p>

<h3 id="why-webp">Why WebP?</h3>

<p>WebP provides several advantages over traditional image formats:</p>

<ul>
  <li><strong>Superior compression:</strong> Reduces file sizes by 25-35% compared to JPEG at equivalent quality</li>
  <li><strong>Transparency support:</strong> Like PNG, but with better compression</li>
  <li><strong>Animation support:</strong> Can replace animated GIFs with better compression</li>
  <li><strong>Broad browser support:</strong> Supported by all modern browsers since 2020</li>
</ul>

<h3 id="basic-conversion-command">Basic Conversion Command</h3>

<p>For converting a single file:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre>convert input.jpeg <span class="nt">-quality</span> 80 output.webp
</pre></td></tr></tbody></table></code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">-quality</code> parameter accepts values from 0-100, where higher values preserve more detail but result in larger files. A value of 80 provides an excellent balance between quality and file size.</p>

<h3 id="batch-conversion">Batch Conversion</h3>

<p>To convert all JPEG files in a directory, use the <code class="language-plaintext highlighter-rouge">mogrify</code> command:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="nb">cd </span>raw <span class="o">&amp;&amp;</span> mogrify <span class="nt">-format</span> webp <span class="nt">-quality</span> 80 <span class="nt">-path</span> ../new <span class="k">*</span>.JPEG
</pre></td></tr></tbody></table></code></pre></div></div>

<p>This command:</p>
<ul>
  <li>Creates the output directory if it doesn’t exist</li>
  <li>Changes to the source directory</li>
  <li>Converts all <code class="language-plaintext highlighter-rouge">.JPEG</code> files to WebP format</li>
  <li>Stores the output in the <code class="language-plaintext highlighter-rouge">../new</code> directory</li>
  <li>Preserves original filenames with the new <code class="language-plaintext highlighter-rouge">.webp</code> extension</li>
</ul>

<p>For handling both uppercase and lowercase extensions:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="nb">cd </span>raw <span class="o">&amp;&amp;</span> mogrify <span class="nt">-format</span> webp <span class="nt">-quality</span> 80 <span class="nt">-path</span> ../new <span class="k">*</span>.<span class="o">{</span>JPEG,jpeg<span class="o">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<h2 id="step-2-adding-copyright-metadata">Step 2: Adding Copyright Metadata</h2>

<p>After converting images to WebP, the next step is embedding copyright information into the file metadata. This protects your intellectual property and provides attribution information.</p>

<h3 id="installing-the-latest-exiftool">Installing the Latest ExifTool</h3>

<p>Ubuntu 22.04’s default ExifTool version doesn’t support writing to WebP files. WebP write support was added in ExifTool version 12.46 (October 2022). To install the latest version from source, download <code class="language-plaintext highlighter-rouge">Image-ExifTool-13.38.tar.gz</code> or a newer version from <a href="https://exiftool.org/">https://exiftool.org/</a> and copy it to <code class="language-plaintext highlighter-rouge">/tmp</code>. Then issue the following commands.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
</pre></td><td class="rouge-code"><pre><span class="nb">cd</span> /tmp
<span class="nb">tar</span> <span class="nt">-xzf</span> Image-ExifTool-12.96.tar.gz
<span class="nb">cd </span>Image-ExifTool-12.96
perl Makefile.PL
<span class="nb">sudo </span>make <span class="nb">install</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Verify the installation:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre>exiftool <span class="nt">-ver</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<h3 id="adding-copyright-information">Adding Copyright Information</h3>

<p>To add copyright metadata to your WebP files:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
</pre></td><td class="rouge-code"><pre>exiftool <span class="nt">-overwrite_original</span> <span class="se">\</span>
  <span class="nt">-Copyright</span><span class="o">=</span><span class="s2">"Copyright © 2025 Markus Werle"</span> <span class="se">\</span>
  <span class="nt">-Artist</span><span class="o">=</span><span class="s2">"Markus Werle"</span> <span class="se">\</span>
  <span class="nt">-CopyrightNotice</span><span class="o">=</span><span class="s2">"Copyright © 2025 Markus Werle"</span> <span class="se">\</span>
  assets/images/new/<span class="k">*</span>.webp
</pre></td></tr></tbody></table></code></pre></div></div>

<p>This command sets three metadata fields:</p>
<ul>
  <li><strong>Copyright:</strong> The copyright statement with the proper © symbol (U+00A9)</li>
  <li><strong>Artist:</strong> The creator’s name</li>
  <li><strong>CopyrightNotice:</strong> An additional copyright field for broader compatibility</li>
</ul>

<p>The <code class="language-plaintext highlighter-rouge">-overwrite_original</code> flag prevents ExifTool from creating backup files (<code class="language-plaintext highlighter-rouge">.webp_original</code>).</p>

<h3 id="handling-minor-warnings">Handling Minor Warnings</h3>

<p>You may encounter warnings like <code class="language-plaintext highlighter-rouge">"[minor] Incorrect XMP tag ID"</code> for some images. These warnings indicate improperly formatted XMP metadata in the original files and don’t prevent the copyright information from being written successfully. To suppress these warnings, add the <code class="language-plaintext highlighter-rouge">-m</code> flag:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
</pre></td><td class="rouge-code"><pre>exiftool <span class="nt">-m</span> <span class="nt">-overwrite_original</span> <span class="se">\</span>
  <span class="nt">-Copyright</span><span class="o">=</span><span class="s2">"Copyright © 2025 Markus Werle"</span> <span class="se">\</span>
  <span class="nt">-Artist</span><span class="o">=</span><span class="s2">"Markus Werle"</span> <span class="se">\</span>
  <span class="nt">-CopyrightNotice</span><span class="o">=</span><span class="s2">"Copyright © 2025 Markus Werle"</span> <span class="se">\</span>
  assets/images/new/<span class="k">*</span>.webp
</pre></td></tr></tbody></table></code></pre></div></div>

<p>The warning doesn’t affect the functionality of your images or prevent metadata from being written. If you still feel inclined to fixthe XMP Metadata, you can rewrite their XMP tags to correct the formatting:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre>exiftool <span class="nt">-xmp</span>:all<span class="o">=</span> <span class="nt">-tagsfromfile</span> @ <span class="nt">-xmp</span>:all problematic_file.webp
</pre></td></tr></tbody></table></code></pre></div></div>

<p>This command removes all XMP tags and rewrites them cleanly, which resolves the “Incorrect XMP tag ID” warning.</p>

<h2 id="step-3-automated-script">Step 3: Automated Script</h2>

<p>For repeated use, the following bash script automates the entire conversion and copyright embedding process.</p>

<h3 id="the-complete-script-to_webp_with_copyrightsh">The Complete Script: <code class="language-plaintext highlighter-rouge">to_webp_with_copyright.sh</code></h3>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
</pre></td><td class="rouge-code"><pre><span class="c">#!/bin/bash</span>

<span class="c"># Script: to_webp_with_copyright.sh</span>
<span class="c"># Description: Convert any image file to WebP format and add copyright metadata</span>
<span class="c"># Author: Markus Werle</span>
<span class="c"># Date: 2025-10-02</span>

<span class="nb">set</span> <span class="nt">-euo</span> pipefail

<span class="c"># Constants</span>
<span class="nb">readonly </span><span class="nv">SCRIPT_NAME</span><span class="o">=</span><span class="s2">"</span><span class="si">$(</span><span class="nb">basename</span> <span class="s2">"</span><span class="nv">$0</span><span class="s2">"</span><span class="si">)</span><span class="s2">"</span>
<span class="nb">readonly </span><span class="nv">REQUIRED_EXIFTOOL_VERSION</span><span class="o">=</span><span class="s2">"13.38"</span>
<span class="nb">readonly </span><span class="nv">COPYRIGHT_TEXT</span><span class="o">=</span><span class="s2">"Copyright © 2025 Markus Werle"</span>
<span class="nb">readonly </span><span class="nv">ARTIST</span><span class="o">=</span><span class="s2">"Markus Werle"</span>

<span class="c"># Variables</span>
<span class="nv">INPUT_FILE</span><span class="o">=</span><span class="s2">""</span>
<span class="nv">OUTPUT_FILE</span><span class="o">=</span><span class="s2">""</span>

<span class="c"># Function: Print usage information</span>
usage<span class="o">()</span> <span class="o">{</span>
    <span class="nb">cat</span> <span class="o">&lt;&lt;</span> <span class="no">EOF</span><span class="sh">
Usage: </span><span class="nv">$SCRIPT_NAME</span><span class="sh"> -i|--input &lt;input-file&gt; -o|--output-file &lt;output-file&gt;

Convert any image file to WebP format and add copyright metadata.

Required arguments:
  -i, --input &lt;file&gt;        Input image file (any format supported by ImageMagick)
  -o, --output-file &lt;file&gt;  Output WebP file (will auto-append .webp if missing)

Optional arguments:
  -h, --help               Display this help message and exit

Examples:
  </span><span class="nv">$SCRIPT_NAME</span><span class="sh"> -i photo.jpg -o photo.webp
  </span><span class="nv">$SCRIPT_NAME</span><span class="sh"> --input image.png --output-file converted

Requirements:
  - ImageMagick (convert command)
  - exiftool version &gt;= </span><span class="nv">$REQUIRED_EXIFTOOL_VERSION</span><span class="sh">
</span><span class="no">
EOF
</span>    <span class="nb">exit </span>0
<span class="o">}</span>

<span class="c"># Function: Print error message and exit</span>
error_exit<span class="o">()</span> <span class="o">{</span>
    <span class="nb">echo</span> <span class="s2">"Error: </span><span class="nv">$1</span><span class="s2">"</span> <span class="o">&gt;</span>&amp;2
    <span class="nb">exit </span>1
<span class="o">}</span>

<span class="c"># Function: Compare version numbers (semver compatible)</span>
version_ge<span class="o">()</span> <span class="o">{</span>
    <span class="c"># Returns 0 (true) if $1 &gt;= $2</span>
    <span class="nb">local </span><span class="nv">ver1</span><span class="o">=</span><span class="s2">"</span><span class="nv">$1</span><span class="s2">"</span>
    <span class="nb">local </span><span class="nv">ver2</span><span class="o">=</span><span class="s2">"</span><span class="nv">$2</span><span class="s2">"</span>
    
    <span class="c"># Use sort -V to compare versions</span>
    <span class="k">if</span> <span class="o">[[</span> <span class="s2">"</span><span class="nv">$ver1</span><span class="s2">"</span> <span class="o">==</span> <span class="s2">"</span><span class="nv">$ver2</span><span class="s2">"</span> <span class="o">]]</span><span class="p">;</span> <span class="k">then
        return </span>0
    <span class="k">fi
    
    </span><span class="nb">local </span>highest
    <span class="nv">highest</span><span class="o">=</span><span class="si">$(</span><span class="nb">echo</span> <span class="nt">-e</span> <span class="s2">"</span><span class="nv">$ver1</span><span class="se">\n</span><span class="nv">$ver2</span><span class="s2">"</span> | <span class="nb">sort</span> <span class="nt">-V</span> | <span class="nb">tail</span> <span class="nt">-n</span> 1<span class="si">)</span>
    
    <span class="o">[[</span> <span class="s2">"</span><span class="nv">$ver1</span><span class="s2">"</span> <span class="o">==</span> <span class="s2">"</span><span class="nv">$highest</span><span class="s2">"</span> <span class="o">]]</span>
<span class="o">}</span>

<span class="c"># Function: Check if required commands exist</span>
check_dependencies<span class="o">()</span> <span class="o">{</span>
    <span class="c"># Check for ImageMagick convert</span>
    <span class="k">if</span> <span class="o">!</span> <span class="nb">command</span> <span class="nt">-v</span> convert &amp;&gt; /dev/null<span class="p">;</span> <span class="k">then
        </span>error_exit <span class="s2">"ImageMagick 'convert' command not found. Please install ImageMagick."</span>
    <span class="k">fi</span>
    
    <span class="c"># Check for exiftool</span>
    <span class="k">if</span> <span class="o">!</span> <span class="nb">command</span> <span class="nt">-v</span> exiftool &amp;&gt; /dev/null<span class="p">;</span> <span class="k">then
        </span>error_exit <span class="s2">"exiftool not found. Please install exiftool version </span><span class="nv">$REQUIRED_EXIFTOOL_VERSION</span><span class="s2"> or higher."</span>
    <span class="k">fi</span>
    
    <span class="c"># Check exiftool version</span>
    <span class="nb">local </span>exiftool_version
    <span class="nv">exiftool_version</span><span class="o">=</span><span class="si">$(</span>exiftool <span class="nt">-ver</span> 2&gt;/dev/null<span class="si">)</span> <span class="o">||</span> error_exit <span class="s2">"Failed to determine exiftool version."</span>
    
    <span class="k">if</span> <span class="o">!</span> version_ge <span class="s2">"</span><span class="nv">$exiftool_version</span><span class="s2">"</span> <span class="s2">"</span><span class="nv">$REQUIRED_EXIFTOOL_VERSION</span><span class="s2">"</span><span class="p">;</span> <span class="k">then
        </span>error_exit <span class="s2">"exiftool version </span><span class="nv">$exiftool_version</span><span class="s2"> is too old. Required: &gt;= </span><span class="nv">$REQUIRED_EXIFTOOL_VERSION</span><span class="s2">"</span>
    <span class="k">fi
    
    </span><span class="nb">echo</span> <span class="s2">"Dependencies check passed: exiftool v</span><span class="nv">$exiftool_version</span><span class="s2">"</span> <span class="o">&gt;</span>&amp;2
<span class="o">}</span>

<span class="c"># Function: Validate input file</span>
validate_input<span class="o">()</span> <span class="o">{</span>
    <span class="nb">local </span><span class="nv">file</span><span class="o">=</span><span class="s2">"</span><span class="nv">$1</span><span class="s2">"</span>
    
    <span class="k">if</span> <span class="o">[[</span> <span class="nt">-z</span> <span class="s2">"</span><span class="nv">$file</span><span class="s2">"</span> <span class="o">]]</span><span class="p">;</span> <span class="k">then
        </span>error_exit <span class="s2">"Input file not specified. Use -i or --input option."</span>
    <span class="k">fi
    
    if</span> <span class="o">[[</span> <span class="o">!</span> <span class="nt">-f</span> <span class="s2">"</span><span class="nv">$file</span><span class="s2">"</span> <span class="o">]]</span><span class="p">;</span> <span class="k">then
        </span>error_exit <span class="s2">"Input file does not exist: </span><span class="nv">$file</span><span class="s2">"</span>
    <span class="k">fi
    
    if</span> <span class="o">[[</span> <span class="o">!</span> <span class="nt">-r</span> <span class="s2">"</span><span class="nv">$file</span><span class="s2">"</span> <span class="o">]]</span><span class="p">;</span> <span class="k">then
        </span>error_exit <span class="s2">"Input file is not readable: </span><span class="nv">$file</span><span class="s2">"</span>
    <span class="k">fi</span>
    
    <span class="c"># Check if ImageMagick can identify the file</span>
    <span class="k">if</span> <span class="o">!</span> identify <span class="s2">"</span><span class="nv">$file</span><span class="s2">"</span> &amp;&gt; /dev/null<span class="p">;</span> <span class="k">then
        </span>error_exit <span class="s2">"Input file is not a valid image format: </span><span class="nv">$file</span><span class="s2">"</span>
    <span class="k">fi</span>
<span class="o">}</span>

<span class="c"># Function: Prepare output filename</span>
prepare_output_filename<span class="o">()</span> <span class="o">{</span>
    <span class="nb">local </span><span class="nv">output</span><span class="o">=</span><span class="s2">"</span><span class="nv">$1</span><span class="s2">"</span>
    
    <span class="k">if</span> <span class="o">[[</span> <span class="nt">-z</span> <span class="s2">"</span><span class="nv">$output</span><span class="s2">"</span> <span class="o">]]</span><span class="p">;</span> <span class="k">then
        </span>error_exit <span class="s2">"Output file not specified. Use -o or --output-file option."</span>
    <span class="k">fi</span>
    
    <span class="c"># Add .webp extension if not present</span>
    <span class="k">if</span> <span class="o">[[</span> <span class="s2">"</span><span class="nv">$output</span><span class="s2">"</span> <span class="o">!=</span> <span class="k">*</span>.webp <span class="o">]]</span><span class="p">;</span> <span class="k">then
        </span><span class="nv">output</span><span class="o">=</span><span class="s2">"</span><span class="k">${</span><span class="nv">output</span><span class="k">}</span><span class="s2">.webp"</span>
        <span class="nb">echo</span> <span class="s2">"Auto-appending .webp extension: </span><span class="nv">$output</span><span class="s2">"</span> <span class="o">&gt;</span>&amp;2
    <span class="k">fi</span>
    
    <span class="c"># Check if output directory exists</span>
    <span class="nb">local </span>output_dir
    <span class="nv">output_dir</span><span class="o">=</span><span class="si">$(</span><span class="nb">dirname</span> <span class="s2">"</span><span class="nv">$output</span><span class="s2">"</span><span class="si">)</span>
    
    <span class="k">if</span> <span class="o">[[</span> <span class="o">!</span> <span class="nt">-d</span> <span class="s2">"</span><span class="nv">$output_dir</span><span class="s2">"</span> <span class="o">]]</span><span class="p">;</span> <span class="k">then
        </span>error_exit <span class="s2">"Output directory does not exist: </span><span class="nv">$output_dir</span><span class="s2">"</span>
    <span class="k">fi
    
    if</span> <span class="o">[[</span> <span class="o">!</span> <span class="nt">-w</span> <span class="s2">"</span><span class="nv">$output_dir</span><span class="s2">"</span> <span class="o">]]</span><span class="p">;</span> <span class="k">then
        </span>error_exit <span class="s2">"Output directory is not writable: </span><span class="nv">$output_dir</span><span class="s2">"</span>
    <span class="k">fi
    
    </span><span class="nb">echo</span> <span class="s2">"</span><span class="nv">$output</span><span class="s2">"</span>
<span class="o">}</span>

<span class="c"># Function: Convert image to WebP</span>
convert_to_webp<span class="o">()</span> <span class="o">{</span>
    <span class="nb">local </span><span class="nv">input</span><span class="o">=</span><span class="s2">"</span><span class="nv">$1</span><span class="s2">"</span>
    <span class="nb">local </span><span class="nv">output</span><span class="o">=</span><span class="s2">"</span><span class="nv">$2</span><span class="s2">"</span>
    
    <span class="nb">echo</span> <span class="s2">"Converting </span><span class="nv">$input</span><span class="s2"> to WebP format..."</span> <span class="o">&gt;</span>&amp;2
    
    <span class="k">if</span> <span class="o">!</span> convert <span class="s2">"</span><span class="nv">$input</span><span class="s2">"</span> <span class="nt">-quality</span> 90 <span class="s2">"</span><span class="nv">$output</span><span class="s2">"</span> 2&gt;/dev/null<span class="p">;</span> <span class="k">then
        </span>error_exit <span class="s2">"Failed to convert image to WebP format."</span>
    <span class="k">fi
    
    if</span> <span class="o">[[</span> <span class="o">!</span> <span class="nt">-f</span> <span class="s2">"</span><span class="nv">$output</span><span class="s2">"</span> <span class="o">]]</span><span class="p">;</span> <span class="k">then
        </span>error_exit <span class="s2">"Conversion completed but output file not found: </span><span class="nv">$output</span><span class="s2">"</span>
    <span class="k">fi
    
    </span><span class="nb">echo</span> <span class="s2">"Conversion successful: </span><span class="nv">$output</span><span class="s2">"</span> <span class="o">&gt;</span>&amp;2
<span class="o">}</span>

<span class="c"># Function: Add copyright metadata</span>
add_copyright_metadata<span class="o">()</span> <span class="o">{</span>
    <span class="nb">local </span><span class="nv">file</span><span class="o">=</span><span class="s2">"</span><span class="nv">$1</span><span class="s2">"</span>
    
    <span class="nb">echo</span> <span class="s2">"Adding copyright metadata..."</span> <span class="o">&gt;</span>&amp;2
    
    <span class="k">if</span> <span class="o">!</span> exiftool <span class="nt">-m</span> <span class="nt">-overwrite_original</span> <span class="se">\</span>
        <span class="nt">-Copyright</span><span class="o">=</span><span class="s2">"</span><span class="nv">$COPYRIGHT_TEXT</span><span class="s2">"</span> <span class="se">\</span>
        <span class="nt">-Artist</span><span class="o">=</span><span class="s2">"</span><span class="nv">$ARTIST</span><span class="s2">"</span> <span class="se">\</span>
        <span class="nt">-CopyrightNotice</span><span class="o">=</span><span class="s2">"</span><span class="nv">$COPYRIGHT_TEXT</span><span class="s2">"</span> <span class="se">\</span>
        <span class="s2">"</span><span class="nv">$file</span><span class="s2">"</span> <span class="o">&gt;</span> /dev/null 2&gt;&amp;1<span class="p">;</span> <span class="k">then
        </span>error_exit <span class="s2">"Failed to add copyright metadata to: </span><span class="nv">$file</span><span class="s2">"</span>
    <span class="k">fi
    
    </span><span class="nb">echo</span> <span class="s2">"Copyright metadata added successfully."</span> <span class="o">&gt;</span>&amp;2
<span class="o">}</span>

<span class="c"># Function: Parse command line arguments</span>
parse_arguments<span class="o">()</span> <span class="o">{</span>
    <span class="k">if</span> <span class="o">[[</span> <span class="nv">$# </span><span class="nt">-eq</span> 0 <span class="o">]]</span><span class="p">;</span> <span class="k">then
        </span>usage
    <span class="k">fi
    
    while</span> <span class="o">[[</span> <span class="nv">$# </span><span class="nt">-gt</span> 0 <span class="o">]]</span><span class="p">;</span> <span class="k">do
        case</span> <span class="s2">"</span><span class="nv">$1</span><span class="s2">"</span> <span class="k">in</span>
            <span class="nt">-h</span><span class="p">|</span><span class="nt">--help</span><span class="p">)</span>
                usage
                <span class="p">;;</span>
            <span class="nt">-i</span><span class="p">|</span><span class="nt">--input</span><span class="p">)</span>
                <span class="k">if</span> <span class="o">[[</span> <span class="nt">-n</span> <span class="s2">"</span><span class="k">${</span><span class="nv">2</span><span class="k">:-}</span><span class="s2">"</span> <span class="o">]]</span> <span class="o">&amp;&amp;</span> <span class="o">[[</span> <span class="s2">"</span><span class="k">${</span><span class="nv">2</span>:0:1<span class="k">}</span><span class="s2">"</span> <span class="o">!=</span> <span class="s2">"-"</span> <span class="o">]]</span><span class="p">;</span> <span class="k">then
                    </span><span class="nv">INPUT_FILE</span><span class="o">=</span><span class="s2">"</span><span class="nv">$2</span><span class="s2">"</span>
                    <span class="nb">shift </span>2
                <span class="k">else
                    </span>error_exit <span class="s2">"Option </span><span class="nv">$1</span><span class="s2"> requires an argument."</span>
                <span class="k">fi</span>
                <span class="p">;;</span>
            <span class="nt">-o</span><span class="p">|</span><span class="nt">--output-file</span><span class="p">)</span>
                <span class="k">if</span> <span class="o">[[</span> <span class="nt">-n</span> <span class="s2">"</span><span class="k">${</span><span class="nv">2</span><span class="k">:-}</span><span class="s2">"</span> <span class="o">]]</span> <span class="o">&amp;&amp;</span> <span class="o">[[</span> <span class="s2">"</span><span class="k">${</span><span class="nv">2</span>:0:1<span class="k">}</span><span class="s2">"</span> <span class="o">!=</span> <span class="s2">"-"</span> <span class="o">]]</span><span class="p">;</span> <span class="k">then
                    </span><span class="nv">OUTPUT_FILE</span><span class="o">=</span><span class="s2">"</span><span class="nv">$2</span><span class="s2">"</span>
                    <span class="nb">shift </span>2
                <span class="k">else
                    </span>error_exit <span class="s2">"Option </span><span class="nv">$1</span><span class="s2"> requires an argument."</span>
                <span class="k">fi</span>
                <span class="p">;;</span>
            <span class="k">*</span><span class="p">)</span>
                error_exit <span class="s2">"Unknown option: </span><span class="nv">$1</span><span class="s2">. Use -h or --help for usage information."</span>
                <span class="p">;;</span>
        <span class="k">esac</span>
    <span class="k">done</span>
    
    <span class="c"># Validate that required arguments are provided</span>
    <span class="k">if</span> <span class="o">[[</span> <span class="nt">-z</span> <span class="s2">"</span><span class="nv">$INPUT_FILE</span><span class="s2">"</span> <span class="o">]]</span><span class="p">;</span> <span class="k">then
        </span>error_exit <span class="s2">"Missing required option: -i|--input. Use -h for help."</span>
    <span class="k">fi
    
    if</span> <span class="o">[[</span> <span class="nt">-z</span> <span class="s2">"</span><span class="nv">$OUTPUT_FILE</span><span class="s2">"</span> <span class="o">]]</span><span class="p">;</span> <span class="k">then
        </span>error_exit <span class="s2">"Missing required option: -o|--output-file. Use -h for help."</span>
    <span class="k">fi</span>
<span class="o">}</span>

<span class="c"># Main execution</span>
main<span class="o">()</span> <span class="o">{</span>
    <span class="c"># Parse command line arguments</span>
    parse_arguments <span class="s2">"</span><span class="nv">$@</span><span class="s2">"</span>
    
    <span class="c"># Check dependencies</span>
    check_dependencies
    
    <span class="c"># Validate input file</span>
    validate_input <span class="s2">"</span><span class="nv">$INPUT_FILE</span><span class="s2">"</span>
    
    <span class="c"># Prepare output filename</span>
    <span class="nv">OUTPUT_FILE</span><span class="o">=</span><span class="si">$(</span>prepare_output_filename <span class="s2">"</span><span class="nv">$OUTPUT_FILE</span><span class="s2">"</span><span class="si">)</span>
    
    <span class="c"># Convert to WebP</span>
    convert_to_webp <span class="s2">"</span><span class="nv">$INPUT_FILE</span><span class="s2">"</span> <span class="s2">"</span><span class="nv">$OUTPUT_FILE</span><span class="s2">"</span>
    
    <span class="c"># Add copyright metadata</span>
    add_copyright_metadata <span class="s2">"</span><span class="nv">$OUTPUT_FILE</span><span class="s2">"</span>
    
    <span class="nb">echo</span> <span class="s2">"✓ Successfully created: </span><span class="nv">$OUTPUT_FILE</span><span class="s2">"</span> <span class="o">&gt;</span>&amp;2
    <span class="nb">exit </span>0
<span class="o">}</span>

<span class="c"># Run main function with all arguments</span>
main <span class="s2">"</span><span class="nv">$@</span><span class="s2">"</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<h3 id="script-features">Script Features</h3>

<p>The script performs several tasks:</p>

<p><strong>Argument Parsing:</strong> Supports both short (<code class="language-plaintext highlighter-rouge">-i</code>, <code class="language-plaintext highlighter-rouge">-o</code>, <code class="language-plaintext highlighter-rouge">-h</code>) and long options (<code class="language-plaintext highlighter-rouge">--input</code>, <code class="language-plaintext highlighter-rouge">--output-file</code>, <code class="language-plaintext highlighter-rouge">--help</code>) with comprehensive validation.</p>

<p><strong>Version Checking:</strong> Automatically verifies that ExifTool version 13.38 or higher is installed using semantic version comparison with the <code class="language-plaintext highlighter-rouge">sort -V</code> method.</p>

<p><strong>Comprehensive Error Handling:</strong> Validates all potential failure points including:</p>
<ul>
  <li>Missing or incorrect command-line arguments</li>
  <li>Non-existent or unreadable input files</li>
  <li>Invalid image formats</li>
  <li>Missing or unwritable output directories</li>
  <li>Conversion failures</li>
  <li>Metadata writing failures</li>
</ul>

<p><strong>Automatic File Extension:</strong> If the output filename doesn’t end with <code class="language-plaintext highlighter-rouge">.webp</code>, the script automatically appends the extension.</p>

<h3 id="usage-examples">Usage Examples</h3>

<p>Make the script executable:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="nb">chmod</span> +x to_webp_with_copyright.sh
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Convert a single image:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre>./to_webp_with_copyright.sh <span class="nt">-i</span> photo.JPEG <span class="nt">-o</span> photo.webp
</pre></td></tr></tbody></table></code></pre></div></div>

<p>The script automatically appends <code class="language-plaintext highlighter-rouge">.webp</code> if omitted:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
</pre></td><td class="rouge-code"><pre>./to_webp_with_copyright.sh <span class="nt">--input</span> image.png <span class="nt">--output-file</span> converted
<span class="c"># Creates: converted.webp</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Display help information:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre>./to_webp_with_copyright.sh <span class="nt">--help</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<h2 id="verification">Verification</h2>

<p>To verify that the copyright metadata was successfully embedded, use ExifTool to inspect the output file:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre>exiftool <span class="nt">-Copyright</span> <span class="nt">-Artist</span> <span class="nt">-CopyrightNotice</span> photo.webp
</pre></td></tr></tbody></table></code></pre></div></div>

<p>This displays the three copyright-related metadata fields you added.</p>]]></content><author><name>Markus Werle</name></author><category term="tutorial" /><category term="image-processing" /><category term="ImageMagick" /><category term="WebP" /><category term="Bash" /><category term="Linux" /><category term="Copyright" /><summary type="html"><![CDATA[A comprehensive guide to batch converting images to WebP format and embedding copyright metadata using ImageMagick and ExifTool on Ubuntu Linux.]]></summary></entry><entry><title type="html">The Case against JIRA - Part 2 of Many</title><link href="https://www.daixtrose.de/management/the-case-against-jira-part-2/" rel="alternate" type="text/html" title="The Case against JIRA - Part 2 of Many" /><published>2024-04-14T00:00:00+00:00</published><updated>2024-04-14T00:00:00+00:00</updated><id>https://www.daixtrose.de/management/the-case-against-jira-part-2</id><content type="html" xml:base="https://www.daixtrose.de/management/the-case-against-jira-part-2/"><![CDATA[<p class="notice--primary">This article is part 2 of a series. Before reading it, please read <a href="/management/the-case-against-jira-part-1">part 1</a>.</p>

<h2 id="tldr">TL;DR</h2>

<p>In the second part of this article series, I will compare JIRA to one of its competitors, <a href="https://github.com">GitHub</a>. The differences in architecture, data model, and UI design are striking. They reveal something very fundamental about software design and about company culture.</p>

<h2 id="disclaimers">Disclaimers</h2>

<ul>
  <li>I am still waiting to get paid for praising some parts of GitHub. 😇</li>
  <li>I am disappointed with how GitHub continues to evolve after <a href="https://news.microsoft.com/announcement/microsoft-acquires-github/">Microsoft acquired GitHub</a>.</li>
  <li>This article is highly opinionated. Please let me know if you disagree on <a href="https://www.linkedin.com/in/markus-werle">LinkedIn</a>.</li>
</ul>

<h2 id="the-power-of-minimalism">The Power of Minimalism</h2>

<h3 id="new-kid-on-the-block">New Kid On The Block</h3>

<p>A couple of years after starting my <a href="/management/the-case-against-jira-part-1/#a-real-life-struggle">struggle with JIRA</a>, I learned about another platform solution that had gained quite some attraction: <a href="https://github.com">GitHub</a>. Getting to know GitHub in its early years, I realized something significant.</p>

<p>When it comes to software tools, simplicity often reigns supreme. GitHub, the popular version control and collaboration platform, exemplified this principle.</p>

<h3 id="github---a-multiverse-of-good-design-decisions">GitHub - A Multiverse of Good Design Decisions</h3>

<p>While GitHub representatives probably never explicitly mentioned the KISS (Keep It Simple, Stupid) principle, the original GitHub design demonstrates a pragmatic approach that values simplicity without compromising functionality. I believe this to be a key factor in GitHub’s meteoric rise and in its becoming the number one place to go for Source Code Management.</p>

<p>GitHub’s success can be attributed to two critical effects of its minimalistic approach:</p>

<ol>
  <li><strong>Ease of Learning</strong>: GitHub’s user-friendly interface made it accessible to experts <em>and</em> novices.</li>
  <li><strong>Reduced Complexity</strong>: GitHub minimized complexity by offering users a streamlined set of options. This simplicity allowed the platform to reach stability and maturity swiftly. Users could focus on their work without being bogged down by unnecessary features.</li>
</ol>

<h3 id="ease-of-learning">Ease of Learning</h3>

<p>I had the chance to see how vital ease of learning can be on several occasions when I  introduced GitHub to technical and non-technical staff simultaneously. As with a chemical reaction, it is crucial to collect sufficient activation energy to get things going.</p>

<p>Interesting enough, using GitHub’s web GUI to create content only requires a fairly low amount of activation energy. People who had never used a markup language to generate formatted text before needed less than an hour to understand how to write issues, READMEs, or Wiki articles.</p>

<p>For <a href="https://www.overleaf.com/">LaTeX text processing</a> fans like myself, the number of features offered by <a href="https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax"><strong>GitHub Flavored Markdown</strong></a> (GFM) are way too limited. But this lightweight markup language can be learned fast by anyone who can read. Its simplicity encourages collaboration and documentation without the need for extensive training.</p>

<p>The positive effect is that non-technical and technical staff can <strong>share</strong> information via a common collaboration platform.</p>

<h3 id="reduced-complexity">Reduced Complexity</h3>

<p>GitHub’s <strong>interface</strong> doesn’t overwhelm users with unnecessary bells and
whistles. It sticks to the essentials. This intentional minimalism prevents
over-engineering and keeps the focus on what truly matters.</p>

<p>Note that GitHub is a tool built on top of one of the world’s most complex and powerful tools, namely <a href="https://git-scm.com/">Git</a>. Git is like the Wenger Giant Swiss Army knife, but all you need is a blade, the nail file, the saw, and the scissors (<code class="language-plaintext highlighter-rouge">git clone</code>, <code class="language-plaintext highlighter-rouge">git add</code>, <code class="language-plaintext highlighter-rouge">git commit</code>, <code class="language-plaintext highlighter-rouge">git push</code>).</p>

<p><img src="/assets/images/webp/Giant_Knife_1.webp" alt="The largest swiss army knife of the world" /> 
<br /><small>Source: <a href="https://upload.wikimedia.org/wikipedia/commons/thumb/7/73/Giant_Knife_1.jpg/1920px-Giant_Knife_1.jpg">Wikipedia</a></small></p>

<p>The GitHub GUI reduces the view of this powerful tool to the absolute necessary.</p>

<h2 id="issue-states">Issue States</h2>

<p>Now, how many states can an issue have? Remember JIRA? Please count <a href="https://support.atlassian.com/jira-cloud-administration/docs/what-are-issue-statuses-priorities-and-resolutions/">all possible “statuses”</a> their documentation lists. I failed to enumerate them all in <a href="/management/the-case-against-jira-part-1/#consistency-nightmare-via-state-overload">part 1</a> of this article series. 
In contrast, the number of possible states of a GitHub issue is at the theoretical minimum. It can exist in only two primary states: <strong>“open”</strong> and <strong>“closed”</strong>.</p>

<p>Of course, this is only half of the truth. As with JIRA tickets, GitHub tickets can have an arbitrary number of labels; from a formal point of view, these are sub-states. Or - to be even more precise - every GitHub issue has one additional substate: a <em>set</em> of labels that can be empty.</p>

<p>Can you spot the difference? The JIRA designers try to catch every possible <strong>view</strong> you can have on an item in one <strong>attribute</strong> - a common error in Object Oriented Programming. Finding the right <em>location</em> for the information to be stored has a significant impact on the scalability of the system itself. JIRA architects failed this task. You have to get structural containment right. The initial error led to further complexity: JIRA does not only have a rather large set of “Issue statuses,” but due to later extension of the system, it also provides <em>additional sets of states</em>: “Jira Software statuses” and “Jira Service Management statuses.”</p>

<p>Is that a complete set of possibilities now? While I was CTO of Ducktrain, we successfully used GitHub Issues for <em>everything</em>, even for the procurement process! How would we handle that in JIRA? Will JIRA extend the <em>set</em> of possible <em>sets</em> of possible “statuses”? Will we have “Jira Procurement Statuses”? Can you see the unnecessary complexity introduced in the system? Can you see Pandora’s box wide open? Is there a better way to map business processes to a ticket system?</p>

<h2 id="distinguish-data-and-views">Distinguish Data and Views</h2>

<p>If you want a certain view of your data, create it! But as a system designer, never try to anticipate and map all possible future usage scenarios of your product into a finite set of states stored within the data items.</p>

<p>Let’s have a look at <a href="https://docs.github.com/en/issues/planning-and-tracking-with-projects/learning-about-projects/about-projects">GitHub Projects</a>. After a considerable launch failure due to a missing m-to-n relation in project views to repositories (which is fixed by now), we have a system that allows a specific set of views (called layouts), such as list views, Kanban boards, and roadmap views.</p>

<p>Interestingly enough, you can <a href="https://docs.github.com/en/issues/planning-and-tracking-with-projects/learning-about-projects/about-projects#adding-metadata-to-your-items">add metadata to an issue</a> like dates or number fields. But this data <em>belongs to the project</em> and is not stored as an issue state in the issue itself.</p>

<p>The column in a Kanban board a ticket is assigned to is also some kind of metadata (an <em>additional state</em> the ticket is in, but <em>not</em> stored as a state <em>within</em> the ticket). Unsurprisingly, a ticket (aka issue) can be in different columns in Kanban views of several projects.</p>

<p>It appears that GitHub’s data model yields a more flexible product and is more robust against changes in the user story.</p>

<h2 id="what-was-it-made-for">What was it made for?</h2>

<!-- Courtesy of embedresponsively.com -->

<div class="responsive-video-container">
    <iframe src="https://www.youtube-nocookie.com/embed/cW8VLC9nnTo" frameborder="0" webkitallowfullscreen="" mozallowfullscreen="" allowfullscreen=""></iframe>
  </div>

<p>The huge success of the Agile Movement may stem from its <strong>human-centric approach</strong>. If you are old enough, you might remember the wishful thinking of <a href="http://agilemanifesto.org/">“Individuals and interactions over processes and tools”</a> that never came true.</p>

<p>GitHub’s huge success may stem from its <strong>developer-centric approach</strong> or, more precisely, from its focus on the people who actually <strong>do</strong> the work, not the ones who <strong>supervise</strong> others.</p>

<p>There is tension between developers’ and managers’ needs. You can guess which party has predominance from the tools an organization uses. For example, extracting metrics and KPIs<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup> is not something a developer needs. It’s what managers need to fake objectivity for their decision-making <sup id="fnref:2" role="doc-noteref"><a href="#fn:2" class="footnote" rel="footnote">2</a></sup> or to feel less insecure.</p>

<p>Don’t get me wrong. The tension between developers’ and managers’ needs does not mean managers’ needs have no right to be fulfilled. Having a clear picture of what is going on in your organization and knowing whether there is enough money to pay the next salary round is essential. <strong>Coordination</strong> between different entities in your organization might serve productivity and unveil potential for improvement.</p>

<p>You should be honest with and aware of the reason for a particular tool choice or tool configuration. Life is hard when a tool does not fit the task, but life is even more challenging if a tool is not used for its intended task but is abused by some people in your organization for supervision or command and <em>control</em>.</p>

<p>So here’s the question: What was GitHub made for? It was made for <strong>collaboration</strong>. It was made for <strong>coordination</strong>. It was made for <strong>documentation</strong>. It was made for <strong>traceability</strong>. It was made for <strong>source code management</strong>.</p>

<p>It was <em>not</em> made for <strong>command and control</strong>. It was <em>not</em> made for <strong>supervising</strong>. It was <em>not</em> made for <strong>performance tracking</strong><sup id="fnref:3" role="doc-noteref"><a href="#fn:3" class="footnote" rel="footnote">3</a></sup>. It was <em>not</em> made for <strong>people management</strong>.</p>

<p>As a manager, I love unified approaches. I love simplified structure and information that fits on a PowerPoint slide because that is what managers do. Three bullet points on a slide to ease <a href="https://www.edwardtufte.com/bboard/q-and-a-fetch-msg?msg_id=0001yB&amp;topic_id=1&amp;topic=Ask+E%2eT%2e">tough decisions</a>. Thinking linear, not thinking complex. It helps when I have 200 decisions to make every day.</p>

<p>Is that what ticket systems were made for? What was JIRA made for? Do you understand how its feature set reflects a particular management style and perspective on a company? At one of my customers, the quality team forced completely independent software development teams to align on their JIRA configuration, renaming states (!) to ease the acquisition of KPI data. This kind of behavior is the complete inversion of <a href="http://agilemanifesto.org/">“Individuals and interactions over processes and tools”</a>.</p>

<p>If you look at GitHub projects with a more critical eye, you can spot the small steps toward features that meet the needs of managers, especially in large organizations. For example, the original GitHub authors turned deaf on any request to add effort estimates to tickets. Now, they are available via the project views. This could be collateral damage of the ongoing <code class="language-plaintext highlighter-rouge">#NoEstimates</code> battle, best illustrated by my most beloved “enemy”  Peter Kretzmann, who gives his website the title “CTO/CIO Perspectives” (I could not have invented this one), <a href="http://www.peterkretzman.com/2014/09/24/the-case-against-noestimates-part-1-introduction-and-common-sense/">fighting bites and nails in an uphill battle</a> against something that worked for GitHub to deliver one of the most valuable software products of the planet.</p>

<p>As with JIRA, the tides have turned for GitHub. The managers are taking over, especially those who think that “if you can’t measure it, you can’t improve it.” Hence, GitHub now provides supervision labeled with the euphemism <a href="https://github.blog/2023-07-19-metrics-for-issues-pull-requests-and-discussions/">“data-driven insights”</a> as if that data really shows you the root cause of your organization’s problems.</p>

<h2 id="some-advice">Some Advice</h2>

<p>If you want to be successful in Software Development (or, more generally speaking, engineering), choose your tools and their configuration so that neither the tools nor the processes designed around them stand in the way of the developers. Drop alleged objectivity through KPIs and other measurements favoring a more human-centric approach.</p>

<p>Yes, try it!</p>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:1" role="doc-endnote">
      <p>KPIs are super attractive because you can make them look like you are progressing as an organization. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:2" role="doc-endnote">
      <p>This is why AI is so attractive. Something that uses math (and some inexplicable magic) sucks information out of data and appears to be objective. Let us not criticize <a href="https://www.technologyreview.com/2020/07/17/1005396/predictive-policing-algorithms-racist-dismantled-machine-learning-bias-criminal-justice/">the way the data was acquired</a>. <a href="#fnref:2" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:3" role="doc-endnote">
      <p>Git, to remain in our original context, was designed for <strong>version control</strong>. Of course, there are toxic people all over the world who seriously think they can gather <a href="https://techcrunch.com/2020/12/24/reading-between-the-lines-how-data-will-optimize-your-developers-annual-reviews/">objective (sic!) insights from git commit data</a>. I hereby put <a href="https://www.linkedin.com/in/alexcircei">Alex Circei</a>, a <em>white</em> man, onto my newly created employee <em>black</em>list. <a href="#fnref:3" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>Markus Werle</name></author><category term="management" /><category term="JIRA" /><category term="GitHub" /></entry><entry><title type="html">The Case against JIRA - Part 1 of Many</title><link href="https://www.daixtrose.de/management/the-case-against-jira-part-1/" rel="alternate" type="text/html" title="The Case against JIRA - Part 1 of Many" /><published>2024-03-16T00:00:00+00:00</published><updated>2024-03-16T00:00:00+00:00</updated><id>https://www.daixtrose.de/management/the-case-against-jira-part-1</id><content type="html" xml:base="https://www.daixtrose.de/management/the-case-against-jira-part-1/"><![CDATA[<p class="notice--primary">If you have never used JIRA, do not read further. Enjoy your life as long as you can, and start screaming while running away when someone proposes to introduce JIRA to your company.</p>

<h2 id="a-real-life-struggle">A Real-Life Struggle</h2>

<p>My first contact with JIRA was while working on a project at a customer’s site. A large automotive company based in southern Germany pushed hard to get a vital project straight. To ease our collaboration, we were granted access to their JIRA instance - or at least parts thereof. Here’s what I witnessed:</p>

<ul>
  <li><strong>Dedicated Maintenance Team</strong>: The company needed a dedicated team solely responsible for maintaining the JIRA instance. Customizations, plugins, and integrations demanded constant attention.</li>
  <li><strong>Training Hurdles</strong>: Bringing everyone on the same page required extensive training sessions. Employees had to navigate intricate workflows, custom fields, and permissions. The learning curve was steep and hindered productivity. Nothing worked out of the box. Since no one had the time to explain the company-specific rules (and no one knew them), the whole project team was lost in an overarching manifold of possibilities.</li>
</ul>

<p>I was concerned. None of the ticket systems I had used so far had been so hard to use compared to JIRA. I had a bad feeling about it, but I could not explain why. I thought that maybe they got the setup and configuration wrong. Today, I think Atlassian’s product management is to be held accountable for this mess.</p>

<p>The decision for a specific tool is crucial for your company’s success. You might guess by the title of this article series and my wording so far that I consider using JIRA for tracking issues extremely harmful. Here is why:</p>

<h2 id="jira---a-multiverse-of-complexities">JIRA - A Multiverse of Complexities</h2>

<p>Let’s venture into the labyrinth of JIRA, the powerful but intricate project management tool. Brace yourself for its complexities:</p>

<h3 id="editor-ui">Editor UI</h3>

<p>JIRA’s editing experience leaves much to be desired. Creating and updating issues can feel cumbersome. The unintuitive behavior of its rich text editor and pseudo-markdown editor may frustrate even seasoned users.</p>

<p>If you have JIRA installed, then check your instance. Do people create easy-to-read descriptions? Do weblinks always work? Is code put in a code block? Can you enumerate all possibilities to configure your text editor (<code class="language-plaintext highlighter-rouge">{code}</code> macro, Wiki Style Renderer)? Are you sure the text editor configuration is consistent across the complete organization? How many hours did your company spend on agreeing on a text editor configuration?</p>

<h3 id="issue-types-and-the-layered-thinking-they-yield">Issue Types And The Layered Thinking They Yield</h3>

<p>Issues or Tickets? Beware! JIRA doesn’t settle for a single issue type. We are pros, right? Instead, it offers a smorgasbord: <strong>Initiatives, Epics, Stories, Tasks, and Sub-tasks</strong>. Each type represents a layer of granularity, forcing users to map their complex reality into this predefined hierarchy.</p>

<p>There’s a saying, “To understand recursion, you must first understand recursion.” Obviously, neither Atlassian’s product managers nor 90% of the so-called Agile Coaches have the slightest clue about recursive structures.</p>

<p>They do not see how, from one issue or ticket, a complete tree structure of child issues could be derived and that the depth of this tree structure is arbitrary due to the nature of the problems or the tasks they describe - and <a href="https://en.wikipedia.org/wiki/Conway%27s_law">due to the setup of the organization</a> working on it. However, the designers of Atlassian deliberately decided that each problem on this planet could be mapped into a finite set of categories. It’s a severe crime, IMHO.</p>

<p>Check for the symptoms: If you use JIRA in your daily business, does your thought process become a game of JIRA Jenga? Do discussions devolve into fruitless debates about categorization rather than solving actual problems? Is it a value in itself to have meetings and lengthy discussions to clarify the arbitrary rules for mapping reality into five categories?</p>

<p>I observed another problem: A specific type of person in your organization (mainly managers, especially those who do not have a master’s in computer science or never wrote more than 100 lines of code) will love this system because it comes with a plethora of arbitrary, hard-to-remember, user-definable rules that can be written to a Confluence page where no one will read them. But anytime someone breaks the rules, these people can re-post the link to that Wiki page and feel superior for at least knowing something.</p>

<p>At least knowing something is very important when you have no clue about tree structures, category theory, or the software architecture and programming skills your team needs to craft the product for which the deadline is already overdue.</p>

<h3 id="consistency-nightmare-via-state-overload">Consistency Nightmare via State Overload</h3>

<p>In their viciousness, the DFH (designers from hell) at Atlassian found another dimension where they could add an even higher level of unnecessary complexity. Buckle up for the state options! A JIRA ticket can have more states than a chameleon has colors. <strong>“To Do,” “In Progress,” “Blocked,” “Review,” “Done,” “Awaiting Approval,”</strong> and the list goes on. But wait, there’s more! The maintainer of a JIRA instance can define additional states and transitions <em>per project</em> and <em>per issue type</em>.</p>

<p>The problematic part here is that a non-programmer can modify the state machine. Chaos ensues, and ad hoc state machines sprout like weeds. The <a href="https://en.wikipedia.org/wiki/Dunning%E2%80%93Kruger_effect">Dunning–Kruger effect</a> leads people to think they can easily design a state machine with more than two states and more than two state transitions.</p>

<p>Getting these state transitions consistent across projects and issue types becomes a Herculean task. Wait three weeks, find some deviation again, create company rules (do not forget to put them on a Confluence page!), and observe them being broken regularly.</p>

<p>When you try to move tickets across projects, you discover that this fails, or you lose information during the transition. In the best case, you catch the admin of your instance and whisper something nice in their ears so they fix it for you. If not, you have no option but to close the original ticket containing the beginning of the discussion and create a new one within your JIRA section. Problem solved.</p>

<p>Don’t worry, they say. People have absolutely no problem gathering information out of 32 different JIRA tickets, and it never happened that someone fixed an issue incorrectly due to missing an essential, well-hidden detail in one of those Initiatives, Epics, Stories, Tasks, or whatsoever. Unless they do, and the customer with the $10M budget chooses to look elsewhere for people who know their mass from a black hole.</p>

<p>If only those who created the mess suffered the most due to their recklessness. One might get rich by suing Atlassian for all the lifetime lost on this <em>feature</em>.</p>

<p>[TODO: <a href="https://imgflip.com/memegenerator/One-Does-Not-Simply">Generate</a> and add a “Lord of the Rings” meme stating, “One does not simply build a state machine.”]</p>

<h2 id="not-everything-was-bad-in-the-90s">Not Everything Was Bad in the 90s</h2>

<p>I am not kidding. Some things and concepts developed in the 90s were quite good. Like e.g. <a href="https://en.wikipedia.org/wiki/The_Game_(1997_film)">“The Game”</a> or the <a href="https://www.warnerbros.com/movies/matrix">Matrix</a> film.</p>

<p>Unfortunately, it also brought us State Machines, which were quite popular in the last millennium. This happened because they fit well with object-oriented programming thinking, which was popular, too. JIRA’s initial release was in 2002. Some people probably learned about these topics at university, became managers at Atlassian, and then thought that they knew a thing or two. And now we pay for their decisions daily.</p>

<p>Make a test: People who think that object-oriented programming is modern also tend to believe that state machines are excellent. Avoid these people. Get out of their way and find someone to learn from.</p>

<h2 id="a-light-at-the-end-of-the-tunnel">A Light at the End of the Tunnel</h2>

<p>Interestingly, no one except managers was using the Atlassian toolchain at one of my customers. Do you see the pattern here? I managed to get Atlassian tools banned within a couple of weeks.</p>

<p>We transitioned to another, much simpler system, and within three months, all tools and development activities were documented and planned without much hassle within the new tool. We also extended the number of users to non-technical staff.</p>

<p>All you need are tools that do not stand in your way but rather boost your productivity. Read more about that success story in one of my following articles.</p>

<h2 id="takeaway">Takeaway</h2>

<p>In the grand theater of software tools, feature bloat often masquerades
as progress. But beware, every unnecessary knob, button, and dropdown
exacts a toll. Your customer’s productivity wanes, buried under layers
of complexity. So, dear product managers, remember that sometimes less truly is more.</p>

<p class="notice--primary">Continue with <a href="/management/the-case-against-jira-part-2">part 2</a> of this series.</p>]]></content><author><name>Markus Werle</name></author><category term="management" /><category term="JIRA" /><category term="GitHub" /></entry><entry><title type="html">The early bird catches the worm</title><link href="https://www.daixtrose.de/blog/the-early-bird-catches-the-worm/" rel="alternate" type="text/html" title="The early bird catches the worm" /><published>2022-05-04T00:00:00+00:00</published><updated>2022-05-04T00:00:00+00:00</updated><id>https://www.daixtrose.de/blog/the-early-bird-catches-the-worm</id><content type="html" xml:base="https://www.daixtrose.de/blog/the-early-bird-catches-the-worm/"><![CDATA[<p class="notice--primary">The early bird catches the worm but how about the dragons we slay at night?</p>

<h2 id="a-direct-message-at-night">A Direct Message at Night</h2>

<p>It’s Saturday night 3:00 am. I am awake at my desk digging through stuff from my ever growing TODO list and trying not to panic. I am good at that. I mean: not to panic. It’s the only thing you can do as CTO of a startup. The <a href="https://signal.org/">Signal Messenger</a> notifies me about a new message received. It’s from one of my employees, let’s call him Gregor, as a cross reference to the dragons in the title.</p>

<p>Gregor proudly informs me about a programming task he finished and the problems he solved.</p>

<p>It was quite tricky. That week we had a vivid discussion about the appropriate design, data ownership, delegation and code organization criteria for reusable components, scalability and ease of use. You probably know this kind of discussions. A philosophical contest is nothing compared to that.</p>

<p>I immediately check the <a href="https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/reviewing-changes-in-pull-requests/commenting-on-a-pull-request">pull request</a> and take a glance at the result of this night shift and I feel an urge to immediately give some positive feedback. The problem is not only solved, but all my hard requirements, my extra wishes (I always have those) and my criticism of the former design was taken into account. 100% solved. Extraordinary solution, elegant by design, easy to grok on the library user’s side, catching usage errors <em>at compile time</em>.</p>

<p>This guy is amazing. Outstanding code quality. A solution right on the spot. Not the overcandid and overengineered stuff that sometimes stems from long discussions.</p>

<h2 id="early-in-the-morning">Early in the Morning</h2>

<p>You want to know who Gregor is? Gregor is the employee who overslept two meetings the very same week, one at 10:00 am, the other at 11:00 am. Gregor also is the one who forgot we had high visit during the week and appeared in a track suit.</p>

<p>Can’t be so hard, Gregor? Look, we really need some communication in this interdisciplinary team and E/E was in need of clarification. It’s not helpful you don’t care! It’s not helpful you shrug your shoulders and mumble around in rudeness and disrepect for your team mates - and your boss.</p>

<h2 id="sleeping-it-over">Sleeping it Over</h2>

<p>I made a decision. I did not put my energy into changing other people. I changed myself: All meetings which Gregor had to attend, especially the recurring ones, I moved into the early afternoon<sup id="fnref:5" role="doc-noteref"><a href="#fn:5" class="footnote" rel="footnote">1</a></sup>.</p>

<p>If we had to schedule before noon due to lack of alternatives, I provided Gregor with a verbal warning notice the day before.</p>

<p>Conflict settled.</p>

<h2 id="dissecting-a-message">Dissecting a Message</h2>

<p>If I understand correctly, the sentence</p>
<blockquote>
  <p>The early bird catches the worm.</p>
</blockquote>

<p>is the postulate that if you start working early in the morning, then your productivity will rise and the quality of your work outcome will be leveraged. It is stated in such a form that it sounds like a general truth or a law of nature.</p>

<p>The problem with statements like these is that they get traded by managers around the world without thinking it over. Nobody takes the time to question the amount of truth in this. Nobody cares if this is backed by science<sup id="fnref:3" role="doc-noteref"><a href="#fn:3" class="footnote" rel="footnote">2</a></sup>. Some people even believe that Daylight Saving Time is a thing<sup id="fnref:6" role="doc-noteref"><a href="#fn:6" class="footnote" rel="footnote">3</a></sup>.</p>

<p>In my opinion a <a href="https://en.wikipedia.org/wiki/Nonviolent_Communication">non-violent communication</a> is inappropriate here. Let’s talk plain: <strong>This idea is total and utter bullshit</strong>.</p>

<p>Neither reliability, maintainability, testability, portability, nor reusability of the code depend on the day of time it is created.</p>

<p>If you really want to know what contributes to code quality, probably ask Joel Spolsky<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">4</a></sup>. If you want to make a statement about the correlation between sleeping times and quality of work outcome, probably consider Albert Einstein<sup id="fnref:2" role="doc-noteref"><a href="#fn:2" class="footnote" rel="footnote">5</a></sup>.</p>

<h2 id="nothing-else-matters">Nothing Else Matters</h2>

<p>I also would like to share my own personal view, totally <em>not</em> backed by science, totally anecdotal, after more than 25 years of software engineering:</p>

<p>The quality of work outcome depends on two things<sup id="fnref:4" role="doc-noteref"><a href="#fn:4" class="footnote" rel="footnote">6</a></sup>:</p>

<ol>
  <li>education</li>
  <li>the intrinsic motivation of your developers</li>
</ol>

<h2 id="homework">Homework</h2>

<ul>
  <li>Is the working environment you are creating inclusive?</li>
  <li>Does your way to organize work reflect the fact that <strong>people are different</strong>? Does it take into account that some people would like to start very early in the morning while others start to be productive at 8:00 pm?</li>
  <li>Does your company provide an environment where <a href="https://about.gitlab.com/company/culture/all-remote/asynchronous/">asynchronuous communication</a> is possible?</li>
  <li>Do you embrace and use KPIs? If yes:
    <ul>
      <li>How do you measure work quality?</li>
      <li>How do you measure code quality?</li>
      <li>And <strong>how do you measure intrinsic motivation?</strong></li>
    </ul>
  </li>
</ul>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:5" role="doc-endnote">
      <p>This decision may be <a href="https://www.payscale.com/career-advice/science-says-best-day-time-meeting/">backed by science</a> - and experience from decades at work, of course. <a href="#fnref:5" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:3" role="doc-endnote">
      <p>I searched for scientific research correlating code quality to daytime and couldn’t find something. Maybe <a href="https://www.google.com/search?q=code+quality+depend+on+time+of+the+day">you give it a try yourself</a>. <a href="#fnref:3" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:6" role="doc-endnote">
      <p>Daylight Saving Time is a thing? Spoiler alert: <a href="https://www.nytimes.com/2022/03/11/well/live/daylight-saving-time-standard-time-debate.html#:~:text=Scientists%20say%20that%20a%20permanent,the%20hormone%20that%20triggers%20sleep.">it’s not</a>. <a href="#fnref:6" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:1" role="doc-endnote">
      <p>Recommended reading: <a href="https://www.joelonsoftware.com/2000/08/09/the-joel-test-12-steps-to-better-code/">The Joel Test: 12 Steps to Better Code</a> <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:2" role="doc-endnote">
      <p>Albert Einstein <a href="https://www.bbc.com/future/article/20170612-what-you-can-learn-from-einsteins-quirky-habits#:~:text=He%20reportedly%20slept%20for%20at,American%20today%20(6.8%20hours).">reportedly slept for at least 10 hours per day</a> <a href="#fnref:2" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:4" role="doc-endnote">
      <p>I am keeping the list short, such that it is easy to grok and easy to remember. <a href="#fnref:4" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>Markus Werle</name></author><category term="blog" /><category term="open organization" /><category term="code quality" /><category term="new work" /><summary type="html"><![CDATA[... but how about the dragons we slay at night?]]></summary></entry><entry><title type="html">Crazy Generic Lambdas</title><link href="https://www.daixtrose.de/talk/crazy-generic-lambdas/" rel="alternate" type="text/html" title="Crazy Generic Lambdas" /><published>2021-12-01T00:00:00+00:00</published><updated>2021-12-01T00:00:00+00:00</updated><id>https://www.daixtrose.de/talk/crazy-generic-lambdas</id><content type="html" xml:base="https://www.daixtrose.de/talk/crazy-generic-lambdas/"><![CDATA[<h2 id="talk">Talk</h2>

<p class="notice--primary"><strong>Abstract:</strong> C++ lambdas are cool, but generic lambdas give you superpower.</p>

<div class="notice--success">
<p>On <a href="https://github.com/daixtrose/">my GitHub account</a> you can find 
the <a href="https://github.com/daixtrose/crazy-generic-lambdas">sources</a> for the <a href="/talks/crazy-generic-lambdas/crazy-generic-lambddas.html">slides</a></p>
</div>]]></content><author><name>Markus Werle</name></author><category term="talk" /><category term="C++" /></entry><entry><title type="html">C++ Lambdas for Static Storage</title><link href="https://www.daixtrose.de/coding/cpp-lambdas-for-static-storage/" rel="alternate" type="text/html" title="C++ Lambdas for Static Storage" /><published>2021-10-13T00:00:00+00:00</published><updated>2021-10-13T00:00:00+00:00</updated><id>https://www.daixtrose.de/coding/cpp-lambdas-for-static-storage</id><content type="html" xml:base="https://www.daixtrose.de/coding/cpp-lambdas-for-static-storage/"><![CDATA[<h2 id="storing-compile-time-values">Storing compile-time values</h2>

<div class="notice--success">
<p>This code is explained in detail in my talk slides <a href="/talk/crazy-generic-lambdas/">“Crazy Generic Lambdas”</a></p>
</div>

<p>Modern C++ has brought us lambdas and <code class="language-plaintext highlighter-rouge">constexpr if</code>. This is good news for compile-time evaluation nerds like me. Now it is possible to seamlessly <a href="https://godbolt.org/#g:!((g:!((g:!((h:codeEditor,i:(filename:'1',fontScale:14,fontUsePx:'0',j:1,lang:c%2B%2B,selection:(endColumn:48,endLineNumber:31,positionColumn:48,positionLineNumber:31,selectionStartColumn:48,selectionStartLineNumber:31,startColumn:48,startLineNumber:31),source:'%23include+%3Ctype_traits%3E%0A%23include+%3Ciostream%3E%0A%0Aconsteval+auto+addItem(auto+origin,+auto+key,+auto+value)+%0A++++requires+(std::is_same_v%3Cvoid,+decltype(origin(key))%3E)+%7B%0A++++%0A++++auto+result+%3D+%5B%3D%5D(auto+arg)+constexpr%0A++++%7B%0A++++++++if+constexpr(std::is_same_v%3Cdecltype(arg),+decltype(key)%3E)+%7B%0A++++++++++++return+value%3B%0A++++++++%7D+%0A++++++++else+if+constexpr(!!std::is_same_v%3Cvoid,+decltype(origin(arg))%3E)%0A++++++++%7B%0A++++++++++++return+origin(arg)%3B++++%0A++++++++%7D%0A++++%7D%3B%0A%0A++++return+result%3B%0A%7D%0A%0Atemplate%3Cint+I%3E+struct+index+%7B%7D%3B%0A%0Aint+main()%0A%7B%0A++++constexpr+auto+c+%3D+%5B%5D(auto)+%7B%7D%3B%0A++++constexpr+auto+c_+%3D+addItem(c,+index%3C1%3E%7B%7D,+%22Hello%22)%3B%0A%0A++++//+Question:+Is+it+OK+that+the+two+types+down+match%3F%0A++++constexpr+auto+c__+%3D+addItem(c_,+index%3C2%3E%7B%7D,+42)%3B++%0A++++%0A++++//+Will+fail+to+compile+as+expected,+since+requires+clause+above+kicks+in%0A++++//+constexpr+auto+c___+%3D+addItem(c__,+index%3C2%3E%7B%7D,+%22WAT%22)%3B++%0A%0A++++std::cout+%3C%3C+c__(index%3C1%3E%7B%7D)+%3C%3C+%22+%22%3B%0A++++std::cout+%3C%3C+c__(index%3C2%3E%7B%7D)+%3C%3C+%22%5Cn%22%3B+++%0A++++%0A++++//+Will+fail+to+compile+as+expected,+since+return+type+is+void+%0A++++//+This+is+the+point+where+I+wish+I+had+a+more+expressive+error+message%0A++++//+std::cout+%3C%3C+c__(index%3C3%3E%7B%7D)+%3C%3C+%22%5Cn%22%3B+++%0A%7D%0A%0A'),l:'5',n:'0',o:'C%2B%2B+source+%231',t:'0')),k:56.937455068296195,l:'4',n:'0',o:'',s:0,t:'0'),(g:!((g:!((h:output,i:(editorid:1,fontScale:14,fontUsePx:'0',j:1,wrap:'0'),l:'5',n:'0',o:'Output+of+x86-64+clang+(trunk)+(Compiler+%231)',t:'0')),header:(),l:'4',m:50,n:'0',o:'',s:0,t:'0'),(g:!((h:compiler,i:(compiler:clang_trunk,filters:(b:'0',binary:'0',commentOnly:'0',demangle:'0',directives:'0',execute:'0',intel:'0',libraryCode:'0',trim:'1'),flagsViewOpen:'1',fontScale:14,fontUsePx:'0',j:1,lang:c%2B%2B,libs:!(),options:'-std%3Dc%2B%2B20',selection:(endColumn:1,endLineNumber:1,positionColumn:1,positionLineNumber:1,selectionStartColumn:1,selectionStartLineNumber:1,startColumn:1,startLineNumber:1),source:1,tree:'1'),l:'5',n:'0',o:'x86-64+clang+(trunk)+(C%2B%2B,+Editor+%231,+Compiler+%231)',t:'0')),k:54.63695183321351,l:'4',m:50,n:'0',o:'',s:0,t:'0')),k:43.062544931703805,l:'3',n:'0',o:'',t:'0')),l:'2',n:'0',o:'',t:'0')),version:4">store key-value pairs within the closures of a lambda</a>:</p>

<div class="language-c++ highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
</pre></td><td class="rouge-code"><pre><span class="cp">#include</span> <span class="cpf">&lt;type_traits&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;iostream&gt;</span><span class="cp">
</span>
<span class="k">consteval</span> <span class="k">auto</span> <span class="nf">addItem</span><span class="p">(</span><span class="k">auto</span> <span class="n">origin</span><span class="p">,</span> <span class="k">auto</span> <span class="n">key</span><span class="p">,</span> <span class="k">auto</span> <span class="n">value</span><span class="p">)</span> 
    <span class="k">requires</span> <span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">is_same_v</span><span class="o">&lt;</span><span class="kt">void</span><span class="p">,</span> <span class="k">decltype</span><span class="p">(</span><span class="n">origin</span><span class="p">(</span><span class="n">key</span><span class="p">))</span><span class="o">&gt;</span><span class="p">)</span> <span class="p">{</span>
    
    <span class="k">auto</span> <span class="n">result</span> <span class="o">=</span> <span class="p">[</span><span class="o">=</span><span class="p">](</span><span class="k">auto</span> <span class="n">arg</span><span class="p">)</span> <span class="k">constexpr</span>
    <span class="p">{</span>
        <span class="k">if</span> <span class="k">constexpr</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">is_same_v</span><span class="o">&lt;</span><span class="k">decltype</span><span class="p">(</span><span class="n">arg</span><span class="p">),</span> <span class="k">decltype</span><span class="p">(</span><span class="n">key</span><span class="p">)</span><span class="o">&gt;</span><span class="p">)</span> <span class="p">{</span>
            <span class="k">return</span> <span class="n">value</span><span class="p">;</span>
        <span class="p">}</span> 
        <span class="k">else</span> <span class="k">if</span> <span class="k">constexpr</span><span class="p">(</span><span class="o">!</span><span class="n">std</span><span class="o">::</span><span class="n">is_same_v</span><span class="o">&lt;</span><span class="kt">void</span><span class="p">,</span> <span class="k">decltype</span><span class="p">(</span><span class="n">origin</span><span class="p">(</span><span class="n">arg</span><span class="p">))</span><span class="o">&gt;</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="k">return</span> <span class="n">origin</span><span class="p">(</span><span class="n">arg</span><span class="p">);</span>    
        <span class="p">}</span>
    <span class="p">};</span>

    <span class="k">return</span> <span class="n">result</span><span class="p">;</span>
<span class="p">}</span>

<span class="k">template</span><span class="o">&lt;</span><span class="kt">int</span> <span class="n">I</span><span class="p">&gt;</span> <span class="k">struct</span> <span class="nc">index</span> <span class="p">{};</span>

<span class="kt">int</span> <span class="n">main</span><span class="p">()</span>
<span class="p">{</span>
    <span class="k">constexpr</span> <span class="k">auto</span> <span class="n">c</span> <span class="o">=</span> <span class="p">[](</span><span class="k">auto</span><span class="p">)</span> <span class="p">{};</span>
    <span class="k">constexpr</span> <span class="k">auto</span> <span class="n">c_</span> <span class="o">=</span> <span class="n">addItem</span><span class="p">(</span><span class="n">c</span><span class="p">,</span> <span class="n">index</span><span class="o">&lt;</span><span class="mi">1</span><span class="o">&gt;</span><span class="p">{},</span> <span class="s">"Hello"</span><span class="p">);</span>
    <span class="k">constexpr</span> <span class="k">auto</span> <span class="n">c__</span> <span class="o">=</span> <span class="n">addItem</span><span class="p">(</span><span class="n">c_</span><span class="p">,</span> <span class="n">index</span><span class="o">&lt;</span><span class="mi">2</span><span class="o">&gt;</span><span class="p">{},</span> <span class="mi">42</span><span class="p">);</span>  
    
    <span class="c1">// Will fail to compile as expected, since requires clause above kicks in</span>
    <span class="c1">// constexpr auto c___ = addItem(c__, index&lt;2&gt;{}, "WAT");  </span>

    <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">c__</span><span class="p">(</span><span class="n">index</span><span class="o">&lt;</span><span class="mi">1</span><span class="o">&gt;</span><span class="p">{})</span> <span class="o">&lt;&lt;</span> <span class="s">" "</span><span class="p">;</span>
    <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">c__</span><span class="p">(</span><span class="n">index</span><span class="o">&lt;</span><span class="mi">2</span><span class="o">&gt;</span><span class="p">{})</span> <span class="o">&lt;&lt;</span> <span class="s">"</span><span class="se">\n</span><span class="s">"</span><span class="p">;</span>   
    
    <span class="c1">// Will fail to compile as expected, since return type is void </span>
    <span class="c1">// std::cout &lt;&lt; c__(index&lt;3&gt;{}) &lt;&lt; "\n";   </span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>]]></content><author><name>Markus Werle</name></author><category term="coding" /><category term="C++" /></entry><entry><title type="html">Saying Goodbye to Dependency Hell</title><link href="https://www.daixtrose.de/talk/saying-goodbye-to-dependency-hell/" rel="alternate" type="text/html" title="Saying Goodbye to Dependency Hell" /><published>2021-06-23T00:00:00+00:00</published><updated>2021-06-23T00:00:00+00:00</updated><id>https://www.daixtrose.de/talk/saying-goodbye-to-dependency-hell</id><content type="html" xml:base="https://www.daixtrose.de/talk/saying-goodbye-to-dependency-hell/"><![CDATA[<h2 id="talk">Talk</h2>

<p class="notice--primary"><strong>Abstract:</strong> Aiming at reusability and maintainability in large scale projects automatically leads to the desire to split the code base into a large number of small modules. This puts more burden on configuration management. The version control system Git allows to address this task via plugins or the built-in submodule management, but these solutions come with some drawbacks regarding performance and usability which leads to acceptance issues and might generate friction in the highly sensible field of collaboration. Fortunately, Modern CMake has some useful features that help to overcome these issues and can make configuration management seamless for both, developers and integrators.</p>

<div class="notice--success">
<p>On <a href="https://github.com/daixtrose/">my GitHub account</a> you can find</p>
<ul>
  <li>the <a href="https://github.com/daixtrose/saying-goodbye-to-dependency-hell">sources</a> for the <a href="/talk/saying-goodbye-to-dependency-hell.html">slides</a></li>
  <li>the <a href="https://github.com/daixtrose/cmake_utilities">proposed CMake utilities</a></li>
</ul>
</div>

<!-- Courtesy of embedresponsively.com -->

<div class="responsive-video-container">
    <iframe src="https://www.youtube-nocookie.com/embed/cPsQ5xYP_48" frameborder="0" webkitallowfullscreen="" mozallowfullscreen="" allowfullscreen=""></iframe>
  </div>]]></content><author><name>Markus Werle</name></author><category term="talk" /><category term="CMake" /><category term="update" /><summary type="html"><![CDATA[... with Git and Modern CMake (And a Shell Script)]]></summary></entry><entry><title type="html">Work In Progress</title><link href="https://www.daixtrose.de/none/this-article-is-work-in-progress/" rel="alternate" type="text/html" title="Work In Progress" /><published>1970-01-01T00:00:00+00:00</published><updated>1970-01-01T00:00:00+00:00</updated><id>https://www.daixtrose.de/none/this-article-is-work-in-progress</id><content type="html" xml:base="https://www.daixtrose.de/none/this-article-is-work-in-progress/"><![CDATA[<p class="notice--primary">This article is not yet published.</p>

<p><img src="/talk/images/DSC_0487_DxO.jpg" alt="work in progress" /></p>]]></content><author><name>Markus Werle</name></author><category term="none" /><category term="none" /></entry></feed>