501 lines
31 KiB
HTML
501 lines
31 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en" class="auto-color">
|
||
<head>
|
||
|
||
<title>Getting a new dev team up to speed</title>
|
||
<meta charset="utf-8" />
|
||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||
<meta name="HandheldFriendly" content="True" />
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||
|
||
<link rel="preload" as="style" href="https://engineering.q42.nl/assets/built/screen.css?v=S0DSiw6jVHJaYsG0" />
|
||
<link rel="preload" as="script" href="https://engineering.q42.nl/assets/built/casper.js?v=YtPhrez3GuGLVDYs" />
|
||
|
||
<link rel="stylesheet" type="text/css" href="https://engineering.q42.nl/assets/built/screen.css?v=S0DSiw6jVHJaYsG0" />
|
||
|
||
<meta name="description" content="How we introduced a tabletop role-playing game as a fun training method for new team members at our Philips Hue project.">
|
||
<link rel="icon" href="https://storage.ghost.io/c/04/1c/041ce2e8-c7a3-4c8c-97dc-ac2f45aad0da/content/images/size/w256h256/2019/09/Q42-Engineering-Blog.png" type="image/png">
|
||
<link rel="canonical" href="https://engineering.q42.nl/production-incidents-training/">
|
||
<meta name="referrer" content="no-referrer-when-downgrade">
|
||
|
||
<meta property="og:site_name" content="Q42 Engineering">
|
||
<meta property="og:type" content="article">
|
||
<meta property="og:title" content="Getting a new dev team up to speed">
|
||
<meta property="og:description" content="How we introduced a tabletop role-playing game as a fun training method for new team members at our Philips Hue project.">
|
||
<meta property="og:url" content="https://engineering.q42.nl/production-incidents-training/">
|
||
<meta property="og:image" content="https://storage.ghost.io/c/04/1c/041ce2e8-c7a3-4c8c-97dc-ac2f45aad0da/content/images/2019/10/A-fun-way-of-training-for-production-incidents.png">
|
||
<meta property="article:published_time" content="2019-10-07T09:45:23.000Z">
|
||
<meta property="article:modified_time" content="2020-05-03T18:03:03.000Z">
|
||
<meta property="article:tag" content="Backend">
|
||
<meta property="article:tag" content="Internet of Things">
|
||
|
||
<meta property="article:publisher" content="https://www.facebook.com/q42bv">
|
||
<meta name="twitter:card" content="summary_large_image">
|
||
<meta name="twitter:title" content="Getting a new dev team up to speed">
|
||
<meta name="twitter:description" content="How we introduced a tabletop role-playing game as a fun training method for new team members at our Philips Hue project.">
|
||
<meta name="twitter:url" content="https://engineering.q42.nl/production-incidents-training/">
|
||
<meta name="twitter:image" content="https://storage.ghost.io/c/04/1c/041ce2e8-c7a3-4c8c-97dc-ac2f45aad0da/content/images/2019/10/A-fun-way-of-training-for-production-incidents.png">
|
||
<meta name="twitter:label1" content="Written by">
|
||
<meta name="twitter:data1" content="Lukas van Driel">
|
||
<meta name="twitter:label2" content="Filed under">
|
||
<meta name="twitter:data2" content="Backend, Internet of Things">
|
||
<meta name="twitter:site" content="@q42">
|
||
<meta property="og:image:width" content="1200">
|
||
<meta property="og:image:height" content="600">
|
||
|
||
<script type="application/ld+json">
|
||
{
|
||
"@context": "https://schema.org",
|
||
"@type": "Article",
|
||
"publisher": {
|
||
"@type": "Organization",
|
||
"name": "Q42 Engineering",
|
||
"url": "https://engineering.q42.nl/",
|
||
"logo": {
|
||
"@type": "ImageObject",
|
||
"url": "https://storage.ghost.io/c/04/1c/041ce2e8-c7a3-4c8c-97dc-ac2f45aad0da/content/images/2020/04/Q42-Engineering-Blog-logo--kleine-versie--2.png"
|
||
}
|
||
},
|
||
"author": {
|
||
"@type": "Person",
|
||
"name": "Lukas van Driel",
|
||
"image": {
|
||
"@type": "ImageObject",
|
||
"url": "//www.gravatar.com/avatar/ba1f214be9e0659204db55839a428b3b?s=250&d=mm&r=x",
|
||
"width": 250,
|
||
"height": 250
|
||
},
|
||
"url": "https://engineering.q42.nl/author/lukas/",
|
||
"sameAs": []
|
||
},
|
||
"headline": "Getting a new dev team up to speed",
|
||
"url": "https://engineering.q42.nl/production-incidents-training/",
|
||
"datePublished": "2019-10-07T09:45:23.000Z",
|
||
"dateModified": "2020-05-03T18:03:03.000Z",
|
||
"image": {
|
||
"@type": "ImageObject",
|
||
"url": "https://storage.ghost.io/c/04/1c/041ce2e8-c7a3-4c8c-97dc-ac2f45aad0da/content/images/size/w1200/2019/10/A-fun-way-of-training-for-production-incidents.png",
|
||
"width": 1200,
|
||
"height": 600
|
||
},
|
||
"keywords": "Backend, Internet of Things",
|
||
"description": "Introducing a tabletop role-playing game as a fun training method for new team members at our Philips Hue project.",
|
||
"mainEntityOfPage": "https://engineering.q42.nl/production-incidents-training/"
|
||
}
|
||
</script>
|
||
|
||
<meta name="generator" content="Ghost 6.64">
|
||
<link rel="alternate" type="application/rss+xml" title="Q42 Engineering" href="https://engineering.q42.nl/rss/">
|
||
<script defer src="https://cdn.jsdelivr.net/ghost/portal@~2.71/umd/portal.min.js" data-i18n="true" data-ghost="https://engineering.q42.nl/" data-key="de06551402dc971d5982113809" data-api="https://q42-engineering.ghost.io/ghost/api/content/" data-locale="en" crossorigin="anonymous"></script><style id="gh-members-styles">.gh-post-upgrade-cta-content,
|
||
.gh-post-upgrade-cta {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
|
||
text-align: center;
|
||
width: 100%;
|
||
color: #ffffff;
|
||
font-size: 16px;
|
||
}
|
||
|
||
.gh-post-upgrade-cta-content {
|
||
border-radius: 8px;
|
||
padding: 40px 4vw;
|
||
}
|
||
|
||
.gh-post-upgrade-cta h2 {
|
||
color: #ffffff;
|
||
font-size: 28px;
|
||
letter-spacing: -0.2px;
|
||
margin: 0;
|
||
padding: 0;
|
||
}
|
||
|
||
.gh-post-upgrade-cta p {
|
||
margin: 20px 0 0;
|
||
padding: 0;
|
||
}
|
||
|
||
.gh-post-upgrade-cta small {
|
||
font-size: 16px;
|
||
letter-spacing: -0.2px;
|
||
}
|
||
|
||
.gh-post-upgrade-cta a {
|
||
color: #ffffff;
|
||
cursor: pointer;
|
||
font-weight: 500;
|
||
box-shadow: none;
|
||
text-decoration: underline;
|
||
}
|
||
|
||
.gh-post-upgrade-cta a:hover {
|
||
color: #ffffff;
|
||
opacity: 0.8;
|
||
box-shadow: none;
|
||
text-decoration: underline;
|
||
}
|
||
|
||
.gh-post-upgrade-cta a.gh-btn {
|
||
display: block;
|
||
background: #ffffff;
|
||
text-decoration: none;
|
||
margin: 28px 0 0;
|
||
padding: 8px 18px;
|
||
border-radius: 4px;
|
||
font-size: 16px;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.gh-post-upgrade-cta a.gh-btn:hover {
|
||
opacity: 0.92;
|
||
}</style>
|
||
<script defer src="https://cdn.jsdelivr.net/ghost/sodo-search@~1.8/umd/sodo-search.min.js" data-key="de06551402dc971d5982113809" data-styles="https://cdn.jsdelivr.net/ghost/sodo-search@~1.8/umd/main.css" data-sodo-search="https://q42-engineering.ghost.io/" data-locale="en" crossorigin="anonymous"></script>
|
||
|
||
<link href="https://engineering.q42.nl/webmentions/receive/" rel="webmention">
|
||
<script defer src="/public/cards.min.js?v=ShRHxgy4po8zN-Wf"></script>
|
||
<link rel="stylesheet" type="text/css" href="/public/cards.min.css?v=WwnU9jw5ancNC8Gc">
|
||
<script defer src="/public/member-attribution.min.js?v=AKG4hWena9j3yX3I"></script>
|
||
<script defer src="/public/ghost-stats.min.js?v=vFcCUf6ZQ0Hyhc8h" data-stringify-payload="false" data-datasource="analytics_events" data-storage="localStorage" data-host="https://engineering.q42.nl/.ghost/analytics/api/v1/page_hit" tb_site_uuid="041ce2e8-c7a3-4c8c-97dc-ac2f45aad0da" tb_post_uuid="10e50bf3-92a2-4635-8efb-f29b0d542d61" tb_post_type="post" tb_member_uuid="undefined" tb_member_status="undefined" tb_gift_link=""></script><style>:root {--ghost-accent-color: #15171A;}</style>
|
||
<!-- Global site tag (gtag.js) - Google Analytics -->
|
||
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-104393396-3"></script>
|
||
<script>
|
||
window.dataLayer = window.dataLayer || [];
|
||
function gtag(){dataLayer.push(arguments);}
|
||
gtag('js', new Date());
|
||
|
||
gtag('config', 'UA-104393396-3',{'anonymize_ip':true});
|
||
console.log("While you're at it, check out our open positions at https://werkenbij.q42.nl/")
|
||
</script>
|
||
|
||
<style>
|
||
.dark-mode ::selection {
|
||
background: #555b5e;
|
||
}
|
||
|
||
@media (prefers-color-scheme: dark) {
|
||
::selection {
|
||
background: #555b5e;
|
||
}
|
||
}
|
||
</style>
|
||
|
||
</head>
|
||
<body class="post-template tag-backend tag-internet-of-things is-head-left-logo has-cover">
|
||
<div class="viewport">
|
||
|
||
<header id="gh-head" class="gh-head outer">
|
||
<div class="gh-head-inner inner">
|
||
<div class="gh-head-brand">
|
||
<a class="gh-head-logo" href="https://engineering.q42.nl">
|
||
<img src="https://storage.ghost.io/c/04/1c/041ce2e8-c7a3-4c8c-97dc-ac2f45aad0da/content/images/2020/04/Q42-Engineering-Blog-logo--kleine-versie--2.png" alt="Q42 Engineering">
|
||
</a>
|
||
<button class="gh-search gh-icon-btn" aria-label="Search this site" data-ghost-search><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2" width="20" height="20"><path stroke-linecap="round" stroke-linejoin="round" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"></path></svg></button>
|
||
<button class="gh-burger" aria-label="Main Menu"></button>
|
||
</div>
|
||
|
||
<nav class="gh-head-menu">
|
||
<ul class="nav">
|
||
<li class="nav-home"><a href="https://engineering.q42.nl/">Home</a></li>
|
||
<li class="nav-ios"><a href="https://engineering.q42.nl/tag/ios/">iOS</a></li>
|
||
<li class="nav-android"><a href="https://engineering.q42.nl/tag/android/">Android</a></li>
|
||
<li class="nav-backend"><a href="https://engineering.q42.nl/tag/backend/">Backend</a></li>
|
||
<li class="nav-frontend"><a href="https://engineering.q42.nl/tag/frontend/">Frontend</a></li>
|
||
<li class="nav-ai"><a href="https://engineering.q42.nl/tag/ai/">AI</a></li>
|
||
<li class="nav-q42-nl"><a href="https://q42.nl/en">q42.nl</a></li>
|
||
<li class="nav-jobs"><a href="https://werkenbij.q42.nl">Jobs</a></li>
|
||
</ul>
|
||
|
||
</nav>
|
||
|
||
<div class="gh-head-actions">
|
||
<button class="gh-search gh-icon-btn" aria-label="Search this site" data-ghost-search><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2" width="20" height="20"><path stroke-linecap="round" stroke-linejoin="round" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"></path></svg></button>
|
||
<div class="gh-head-members">
|
||
<a class="gh-head-link" href="#/portal/signin" data-portal="signin">Sign in</a>
|
||
<a class="gh-head-button" href="#/portal/signup" data-portal="signup">Subscribe</a>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</header>
|
||
|
||
<div class="site-content">
|
||
|
||
|
||
|
||
|
||
<main id="site-main" class="site-main">
|
||
<article class="article post tag-backend tag-internet-of-things ">
|
||
|
||
<header class="article-header gh-canvas">
|
||
|
||
<div class="article-tag post-card-tags">
|
||
<span class="post-card-primary-tag">
|
||
<a href="/tag/backend/">Backend</a>
|
||
</span>
|
||
</div>
|
||
|
||
<h1 class="article-title">"A wild Demogorgon just wrecked your Kubernetes cluster"</h1>
|
||
|
||
<p class="article-excerpt">Introducing a tabletop role-playing game as a fun training method for new team members at our Philips Hue project.</p>
|
||
|
||
<div class="article-byline">
|
||
<section class="article-byline-content">
|
||
|
||
<ul class="author-list instapaper_ignore">
|
||
<li class="author-list-item">
|
||
<a href="/author/lukas/" class="author-avatar" aria-label="Read more of Lukas van Driel">
|
||
<img class="author-profile-image" src="//www.gravatar.com/avatar/ba1f214be9e0659204db55839a428b3b?s=250&d=mm&r=x" alt="Lukas van Driel" loading="eager" />
|
||
</a>
|
||
</li>
|
||
</ul>
|
||
|
||
<div class="article-byline-meta">
|
||
<h4 class="author-name"><a href="/author/lukas/">Lukas van Driel</a></h4>
|
||
<div class="byline-meta-content">
|
||
<time class="byline-meta-date" datetime="2019-10-07">07 Oct 2019</time>
|
||
<span class="byline-reading-time"><span class="bull">•</span> 4 min read</span>
|
||
</div>
|
||
</div>
|
||
|
||
</section>
|
||
<a href="#/share" class="gh-button gh-button-share">Share</a>
|
||
</div>
|
||
|
||
<figure class="article-image">
|
||
<img
|
||
class="gh-feature-image"
|
||
srcset="https://storage.ghost.io/c/04/1c/041ce2e8-c7a3-4c8c-97dc-ac2f45aad0da/content/images/size/w300/2019/10/A-fun-way-of-training-for-production-incidents.png 300w,
|
||
https://storage.ghost.io/c/04/1c/041ce2e8-c7a3-4c8c-97dc-ac2f45aad0da/content/images/size/w600/2019/10/A-fun-way-of-training-for-production-incidents.png 600w,
|
||
https://storage.ghost.io/c/04/1c/041ce2e8-c7a3-4c8c-97dc-ac2f45aad0da/content/images/size/w1000/2019/10/A-fun-way-of-training-for-production-incidents.png 1000w,
|
||
https://storage.ghost.io/c/04/1c/041ce2e8-c7a3-4c8c-97dc-ac2f45aad0da/content/images/size/w2000/2019/10/A-fun-way-of-training-for-production-incidents.png 2000w"
|
||
sizes="(min-width: 1400px) 1400px, 92vw"
|
||
src="https://storage.ghost.io/c/04/1c/041ce2e8-c7a3-4c8c-97dc-ac2f45aad0da/content/images/size/w2000/2019/10/A-fun-way-of-training-for-production-incidents.png"
|
||
alt=""A wild Demogorgon just wrecked your Kubernetes cluster""
|
||
fetchpriority="high"
|
||
loading="eager"
|
||
/>
|
||
</figure>
|
||
|
||
</header>
|
||
|
||
<section class="gh-content gh-canvas">
|
||
<h3 id="a-fun-way-of-training-for-production-incidents">A fun way of training for production incidents</h3><blockquote><strong>Dungeon master</strong>: “You get an email notification from Stackdriver telling you that the amount of 504 errors in the Client Eastwood service is high and rising.”<br><strong>Team</strong>: “View the logs of Client Eastwood to check for anomalies.”<br><strong>Dungeon master</strong>: “There have been <strong>no</strong> Client Eastwood logs for the past 3 minutes.”<br><strong>Team</strong>: 😱</blockquote><p>We just spent an entire day in a war room playing Dungeons and Dragons. Not with Demogorgons and mythical creatures, but with misconfigured kubernetes secrets, rogue firewalls and missing database records.</p><p>I am a part of the team at <a href="https://www.q42.nl/en?ref=engineering.q42.nl">Q42</a> that’s responsible for the <a href="https://www.q42.nl/en/work/philips-hue1?ref=engineering.q42.nl">Philips Hue Cloud infrastructure</a>. Our goal is to have the lights respond to your requests as quickly as possible when you interact with them via Alexa or Google Home.</p><h3 id="a-new-team">A new team</h3><p>Recently there have been a lot of team changes. Two of the most experienced developers have moved on to different teams. That leaves quite a knowledge gap that we’re trying to close. Next to the usual pair programming, PR reviews and ‘Getting to know Hue’ sessions, we initiated two new methods to get the new team up to speed: tabletop role playing and deliberately breaking our acceptance environment.</p><p>We loosely based this on a <a href="https://youtu.be/-JtOKyELlr0?ref=engineering.q42.nl">talk by Franklin Hu</a> at the Lead Developer Conference London this year about how they approach training new team members at Stripe. He in turn got his game day idea from <a href="https://stripe.com/blog/game-day-exercises-at-stripe?ref=engineering.q42.nl">a blog post by Etsy on purposefully breaking things in production</a>.</p><h3 id="tabletop-roleplaying">Tabletop roleplaying</h3><p>The idea is that we hypothesize a scenario where something on production breaks. The dungeon master starts off by notifying the team that there’s a problem. An alert triggers, a product owner calls, we’re deploying a new feature, something of that sort. The team can ask questions to the dungeon master to get a better understanding of what is going on. The goal is to determine what the problem is, and to provide a solution. In the process, the whole team learns about the best way to approach these kinds of issues.</p><p>Dungeon master: “To get inspiration on the scenarios that we could throw at the team, we looked through our post mortem write-ups and selected three interesting ones. The scenarios had manifested themselves, when none of the new team members were on board yet, so they didn’t know (a lot) about these encounters. We had the metric data of those events in Datadog (our monitoring dashboard), so we could show these to the team if they asked to see them.”</p><blockquote><strong>Dungeon Master</strong>: “The product owner calls and says that a significant amount of the client disconnected simultaneously.”<br><strong>Team</strong>: “View the status dashboard.”<br><strong>Dungeon Master</strong>: “All systems are fully operational, there is just a huge amount of clients missing.”<br>…after hypothetically viewing lots of dashboards, logs & databases...<br><strong>Dungeon Master</strong>: “You look at the data of some of the missing clients. Suddenly you see something suspicious: their IP numbers are in the same range…”<br><strong>Team</strong>: “Ah! It’s an outage with an Internet Service Provider!”<br><strong>Dungeon Master</strong>: “Bingo! Half of Germany doesn’t have any internets…”</blockquote><p>Team: “This was very insightful, and good to test out while there is not a real production outage and people breathing down our necks. There's no place like production, but this came pretty close!”</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://storage.ghost.io/c/04/1c/041ce2e8-c7a3-4c8c-97dc-ac2f45aad0da/content/images/2019/09/image1.jpg" class="kg-image" alt loading="lazy"><figcaption><em>Herman role-playing Google Priority Support: “How can I help you?”</em></figcaption></figure><p>Dungeon master: “It was fun to see the team struggle for a while trying to get a sense of what is going on, and the relief when they found a breakthrough”.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://storage.ghost.io/c/04/1c/041ce2e8-c7a3-4c8c-97dc-ac2f45aad0da/content/images/2019/09/image2-1.jpg" class="kg-image" alt loading="lazy"><figcaption><em>“Timeline of events of the virtual production outage”</em></figcaption></figure><h3 id="game-scenarios-or-deliberately-breaking-acceptance">Game scenarios, or Deliberately breaking acceptance</h3><p>There are also situations that can be more easily reproduced on an acceptance environment. Even the product owner was in on it: after lunch he called to say there were complaints from testers that couldn't connect to their Philips Hue bridge on the acceptance environment. Team, figure it out! This turned out to be a NGINX-server that was wrongly configured when routing data into our cluster. It ran outside our cluster and we had no control over it, so providing a quick solution was a challenge.</p><p>Another fun one: the kubernetes command line tool could not successfully send commands to our cluster any more. This turned out to be because of a rogue firewall rule that didn't allow the kubernetes master to access the nodes themselves.</p><h3 id="wrap-up">Wrap-up</h3><p>At the end of the day the new team members had touched parts of the code that they hadn’t known about until then. This significantly improved their knowledge about approaching production issues. We also had a lot of fun learning it. Getting as close to the real thing as we could, really helped the thinking process as well as getting comfortable searching for unknown problems. It’s also a great way to get some hands-on information on parts of the architecture you may not have seen yet.</p><blockquote>It really felt like playing actual DnD! But with our code! Which was on fire!<br>~ Team member Roan</blockquote><h3 id="try-it-yourself-">Try it yourself!</h3><p>This way of training is a great way to pass on knowledge, and it's fun for everyone involved. If you want to feel more comfortable with your team jumping into production issues, this is a great way to get experience in that field without having to wait for production to break. </p><p>If you’re looking for inspiration on tabletop scenarios, check out <a href="https://twitter.com/badthingsdaily?ref=engineering.q42.nl">this Twitter account that posts tabletop scenarios they play at Stripe</a>.</p><hr><p><em>Always wanted to play DnD at work? Check our job vacancy (in Dutch) at <a href="https://werkenbij.q42.nl/backend-developer?ref=engineering.q42.nl">https://werkenbij.q42.nl</a>! :) </em></p>
|
||
</section>
|
||
|
||
|
||
</article>
|
||
</main>
|
||
|
||
<section class="footer-cta outer">
|
||
<div class="inner">
|
||
<h2 class="footer-cta-title">Sign up for more like this.</h2>
|
||
<a class="footer-cta-button" href="#/portal" data-portal>
|
||
<div class="footer-cta-input">Enter your email</div>
|
||
<span>Subscribe</span>
|
||
</a>
|
||
</div>
|
||
</section>
|
||
|
||
|
||
|
||
<aside class="read-more-wrap outer">
|
||
<div class="read-more inner">
|
||
|
||
<article class="post-card post">
|
||
|
||
<a class="post-card-image-link" href="/online-museum-framework/">
|
||
|
||
<img class="post-card-image"
|
||
srcset="https://storage.ghost.io/c/04/1c/041ce2e8-c7a3-4c8c-97dc-ac2f45aad0da/content/images/size/w300/2026/07/Online-Museum-Platform-for-white-label-websites-by-internet-agency-Q42.png 300w,
|
||
https://storage.ghost.io/c/04/1c/041ce2e8-c7a3-4c8c-97dc-ac2f45aad0da/content/images/size/w600/2026/07/Online-Museum-Platform-for-white-label-websites-by-internet-agency-Q42.png 600w,
|
||
https://storage.ghost.io/c/04/1c/041ce2e8-c7a3-4c8c-97dc-ac2f45aad0da/content/images/size/w1000/2026/07/Online-Museum-Platform-for-white-label-websites-by-internet-agency-Q42.png 1000w,
|
||
https://storage.ghost.io/c/04/1c/041ce2e8-c7a3-4c8c-97dc-ac2f45aad0da/content/images/size/w2000/2026/07/Online-Museum-Platform-for-white-label-websites-by-internet-agency-Q42.png 2000w"
|
||
sizes="(max-width: 1000px) 400px, 800px"
|
||
src="https://storage.ghost.io/c/04/1c/041ce2e8-c7a3-4c8c-97dc-ac2f45aad0da/content/images/size/w600/2026/07/Online-Museum-Platform-for-white-label-websites-by-internet-agency-Q42.png"
|
||
alt="Man on a pedestal with strings in his hands, to which he has attached laptops wirh museum websites."
|
||
loading="lazy"
|
||
/>
|
||
|
||
|
||
</a>
|
||
|
||
<div class="post-card-content">
|
||
|
||
<a class="post-card-content-link" href="/online-museum-framework/">
|
||
<header class="post-card-header">
|
||
<div class="post-card-tags">
|
||
</div>
|
||
<h2 class="post-card-title">
|
||
Squarespace without limits for museums
|
||
</h2>
|
||
</header>
|
||
<div class="post-card-excerpt">How we built a next-gen modular framework to power unique museum websites and innovate cultural tech</div>
|
||
</a>
|
||
|
||
<footer class="post-card-meta">
|
||
<time class="post-card-meta-date" datetime="2026-07-20">20 Jul 2026</time>
|
||
<span class="post-card-meta-length">14 min read</span>
|
||
</footer>
|
||
|
||
</div>
|
||
|
||
</article>
|
||
|
||
<article class="post-card post">
|
||
|
||
<a class="post-card-image-link" href="/visual-search/">
|
||
|
||
<img class="post-card-image"
|
||
srcset="https://storage.ghost.io/c/04/1c/041ce2e8-c7a3-4c8c-97dc-ac2f45aad0da/content/images/size/w300/2025/12/AI-Visual-Search-Rijksmuseum-colelction-artworks.jpg 300w,
|
||
https://storage.ghost.io/c/04/1c/041ce2e8-c7a3-4c8c-97dc-ac2f45aad0da/content/images/size/w600/2025/12/AI-Visual-Search-Rijksmuseum-colelction-artworks.jpg 600w,
|
||
https://storage.ghost.io/c/04/1c/041ce2e8-c7a3-4c8c-97dc-ac2f45aad0da/content/images/size/w1000/2025/12/AI-Visual-Search-Rijksmuseum-colelction-artworks.jpg 1000w,
|
||
https://storage.ghost.io/c/04/1c/041ce2e8-c7a3-4c8c-97dc-ac2f45aad0da/content/images/size/w2000/2025/12/AI-Visual-Search-Rijksmuseum-colelction-artworks.jpg 2000w"
|
||
sizes="(max-width: 1000px) 400px, 800px"
|
||
src="https://storage.ghost.io/c/04/1c/041ce2e8-c7a3-4c8c-97dc-ac2f45aad0da/content/images/size/w600/2025/12/AI-Visual-Search-Rijksmuseum-colelction-artworks.jpg"
|
||
alt="Illustration of a man pointing to several artworks with a guitar that is outlined by a bright yellow line."
|
||
loading="lazy"
|
||
/>
|
||
|
||
|
||
</a>
|
||
|
||
<div class="post-card-content">
|
||
|
||
<a class="post-card-content-link" href="/visual-search/">
|
||
<header class="post-card-header">
|
||
<div class="post-card-tags">
|
||
</div>
|
||
<h2 class="post-card-title">
|
||
Expanding visual search for online art collections
|
||
</h2>
|
||
</header>
|
||
<div class="post-card-excerpt">Enhancing art discovery by applying AI on Rijksmuseum's Collection Online</div>
|
||
</a>
|
||
|
||
<footer class="post-card-meta">
|
||
<time class="post-card-meta-date" datetime="2026-01-26">26 Jan 2026</time>
|
||
<span class="post-card-meta-length">14 min read</span>
|
||
</footer>
|
||
|
||
</div>
|
||
|
||
</article>
|
||
|
||
<article class="post-card post">
|
||
|
||
<a class="post-card-image-link" href="/multiplatform/">
|
||
|
||
<img class="post-card-image"
|
||
srcset="https://storage.ghost.io/c/04/1c/041ce2e8-c7a3-4c8c-97dc-ac2f45aad0da/content/images/size/w300/2026/01/2026-10-12---Frank---Kotlin-and-Compose-multiplatform.png 300w,
|
||
https://storage.ghost.io/c/04/1c/041ce2e8-c7a3-4c8c-97dc-ac2f45aad0da/content/images/size/w600/2026/01/2026-10-12---Frank---Kotlin-and-Compose-multiplatform.png 600w,
|
||
https://storage.ghost.io/c/04/1c/041ce2e8-c7a3-4c8c-97dc-ac2f45aad0da/content/images/size/w1000/2026/01/2026-10-12---Frank---Kotlin-and-Compose-multiplatform.png 1000w,
|
||
https://storage.ghost.io/c/04/1c/041ce2e8-c7a3-4c8c-97dc-ac2f45aad0da/content/images/size/w2000/2026/01/2026-10-12---Frank---Kotlin-and-Compose-multiplatform.png 2000w"
|
||
sizes="(max-width: 1000px) 400px, 800px"
|
||
src="https://storage.ghost.io/c/04/1c/041ce2e8-c7a3-4c8c-97dc-ac2f45aad0da/content/images/size/w600/2026/01/2026-10-12---Frank---Kotlin-and-Compose-multiplatform.png"
|
||
alt="Illustration of a person juggling with Android and Apple icons."
|
||
loading="lazy"
|
||
/>
|
||
|
||
|
||
</a>
|
||
|
||
<div class="post-card-content">
|
||
|
||
<a class="post-card-content-link" href="/multiplatform/">
|
||
<header class="post-card-header">
|
||
<div class="post-card-tags">
|
||
</div>
|
||
<h2 class="post-card-title">
|
||
Why we love multiplatform with Kotlin and Compose
|
||
</h2>
|
||
</header>
|
||
<div class="post-card-excerpt">Shared where it makes sense, native where it matters</div>
|
||
</a>
|
||
|
||
<footer class="post-card-meta">
|
||
<time class="post-card-meta-date" datetime="2026-01-12">12 Jan 2026</time>
|
||
<span class="post-card-meta-length">6 min read</span>
|
||
</footer>
|
||
|
||
</div>
|
||
|
||
</article>
|
||
</div>
|
||
</aside>
|
||
|
||
|
||
|
||
</div>
|
||
|
||
<footer class="site-footer outer">
|
||
<div class="inner">
|
||
<section class="copyright"><a href="https://engineering.q42.nl">Q42 Engineering</a> © 2026</section>
|
||
<div class="site-footer-center">
|
||
<div class="site-footer-social-links">
|
||
<a href="https://x.com/q42" target="_blank" rel="noopener" aria-label="X">
|
||
<svg class="icon" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||
<path d="M18.2439 2.25H21.5519L14.3249 10.51L22.8269 21.75H16.1699L10.9559 14.933L4.98991 21.75H1.67991L9.40991 12.915L1.25391 2.25H8.07991L12.7929 8.481L18.2439 2.25ZM17.0829 19.77H18.9159L7.08391 4.126H5.11691L17.0829 19.77Z" fill="currentColor"/>
|
||
</svg> </a>
|
||
<a href="https://www.facebook.com/q42bv" target="_blank" rel="noopener" aria-label="Facebook">
|
||
<svg class="icon" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" fill="currentColor"><path d="M23.9981 11.9991C23.9981 5.37216 18.626 0 11.9991 0C5.37216 0 0 5.37216 0 11.9991C0 17.9882 4.38789 22.9522 10.1242 23.8524V15.4676H7.07758V11.9991H10.1242V9.35553C10.1242 6.34826 11.9156 4.68714 14.6564 4.68714C15.9692 4.68714 17.3424 4.92149 17.3424 4.92149V7.87439H15.8294C14.3388 7.87439 13.8739 8.79933 13.8739 9.74824V11.9991H17.2018L16.6698 15.4676H13.8739V23.8524C19.6103 22.9522 23.9981 17.9882 23.9981 11.9991Z"/></svg> </a>
|
||
</div>
|
||
<nav class="site-footer-nav">
|
||
|
||
</nav>
|
||
</div>
|
||
<div class="gh-powered-by"><a href="https://ghost.org/" target="_blank" rel="noopener">Powered by Ghost</a></div>
|
||
</div>
|
||
</footer>
|
||
|
||
</div>
|
||
|
||
<div class="pswp" tabindex="-1" role="dialog" aria-hidden="true">
|
||
<div class="pswp__bg"></div>
|
||
|
||
<div class="pswp__scroll-wrap">
|
||
<div class="pswp__container">
|
||
<div class="pswp__item"></div>
|
||
<div class="pswp__item"></div>
|
||
<div class="pswp__item"></div>
|
||
</div>
|
||
|
||
<div class="pswp__ui pswp__ui--hidden">
|
||
<div class="pswp__top-bar">
|
||
<div class="pswp__counter"></div>
|
||
|
||
<button class="pswp__button pswp__button--close" title="Close (Esc)"></button>
|
||
<button class="pswp__button pswp__button--share" title="Share"></button>
|
||
<button class="pswp__button pswp__button--fs" title="Toggle fullscreen"></button>
|
||
<button class="pswp__button pswp__button--zoom" title="Zoom in/out"></button>
|
||
|
||
<div class="pswp__preloader">
|
||
<div class="pswp__preloader__icn">
|
||
<div class="pswp__preloader__cut">
|
||
<div class="pswp__preloader__donut"></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="pswp__share-modal pswp__share-modal--hidden pswp__single-tap">
|
||
<div class="pswp__share-tooltip"></div>
|
||
</div>
|
||
|
||
<button class="pswp__button pswp__button--arrow--left" title="Previous (arrow left)"></button>
|
||
<button class="pswp__button pswp__button--arrow--right" title="Next (arrow right)"></button>
|
||
|
||
<div class="pswp__caption">
|
||
<div class="pswp__caption__center"></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<script src="https://engineering.q42.nl/assets/built/casper.js?v=YtPhrez3GuGLVDYs" defer></script>
|
||
|
||
<script src="https://www.qreditroll.com/qreditroll.js"></script>
|
||
<script src="https://d3s4clg74dg0wr.cloudfront.net/widget/js/main.js?v=6.2.0"></script>
|
||
<script>try{Homerun.lang='nl';Homerun.loadWidget('ef00lzqijdkifii5nrq2');}catch(e){};</script>
|
||
|
||
</body>
|
||
</html>
|