tag:blogger.com,1999:blog-47104976529278567892024-03-21T12:38:57.565+01:00Software etc.Random thoughts on software by <a href="https://www.linkedin.com/in/jouke-waleson/">Jouke Waleson</a>, a Fractional CTO in NL. I help organizations in part-time to get the most out of their teams, tech and products while building my own software product.Jouke Walesonhttp://www.blogger.com/profile/12215646682936082041noreply@blogger.comBlogger67125tag:blogger.com,1999:blog-4710497652927856789.post-6233976423727485232024-03-03T17:54:00.004+01:002024-03-03T18:00:28.736+01:00Podcasts<p>The podcast hype train passed me by until late 2018. I never understood it and thought it was some Apple specific thing. Then, one evening, on a flight from Amsterdam to Berlin, I thought "I'm too tired to work or read, but I can listen to an interesting story, let's give podcasts a try."</p><p>The first episode I listened to was by 99% Invisible. I believe that by the time I got back to Amsterdam a few days later I had listened to about 10 episodes. Half a year later and I had gone through 80% of all episodes. I'm a bit obsessive, and as it turns out, picky. For me to love a podcast it has to be:</p><ul style="text-align: left;"><li><b>A</b>uthentic. I have a soft spot for the lone podcaster who loves what he/she is doing.</li><li><b>I</b>nformative. I want to learn new things, and be slightly entertained in the process.</li><li><b>O</b>rchestrated. I can't stand random people talking about something without a clear direction.</li></ul><div>Orchestrated and Authentic can sometimes conflict. It's a sign of good artistic leadership if that doesn't happen.</div><div><br /></div><div>Since my first podcast in 2018, virtually no day has gone by without me listening to a podcast. Most nights I fall asleep while listening to one, which I resume the next day. Either I fall asleep and it doesn't matter, or I lie awake and learn something new. Both are good outcomes.</div><div><br /></div><div>Here are the podcasts that I've listened to a LOT. Thank you to all the hosts, producers and other people involved in creating these.</div><div><br /></div><div>The A / I / O are the aspects above on a 1-5 scale.</div><div><br /></div><div><b>A+ - Have to listen to any new episode:</b></div><div><ul style="text-align: left;"><li>The Memory Palace - A5, I5, O5</li><li>The Fall of Civilizations - A5, I5, O5</li><li>Hardcore History - A5, I5, O5</li><li>The Anthropocene Reviewed - A5, I4, O4</li></ul><div>If you know these podcasts you will recognize a common theme. Nostalgia. Apparently it runs deep with me.</div><div><br /></div><div><b>A - Loved most of the episodes but not all, or I'm slightly tired of it by now.</b></div><ul style="text-align: left;"><li>The Rest is History - A5, I5, O4</li><li>The History of Rome - A5, I4, O5</li><li>In Our Time - A4, I4, O5</li></ul><div>These podcasts have taught me so much about the world. I feel a bit of Renaissance man because of them.</div><div><br /></div><div><div>The Rest is History is actually A+ to me right now, but I know I'll get a bit tired of it eventually. There's there's just so much! I've listened to about 50% of the episodes now. Around 10% of the episodes are not really interesting.</div><div><br /></div><div>One of the best gifts I've ever received is a signed copy of the Rest Is History. Thank you dearest wife! Here it is:</div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhGALArJn1LQzfCvxy6EW_ja_rqdSlTMQfvZWYNhfQzsD-xbYDZCQw6cZEDH8pS-8XTMgZKTwzHIMryg4m-wbPUTpaU8YgVyZbc46cn_P_30EE1pnKOOLr3bq9GCbrHntMUgthH2WxnLcwcz4zqlKbAk65bAlrrb7vIR-NWChrhnPeuvZZoPZjIhz8dvyQ/s2048/WhatsApp%20Image%202024-02-16%20at%2009.17.23.jpeg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="2048" data-original-width="1153" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhGALArJn1LQzfCvxy6EW_ja_rqdSlTMQfvZWYNhfQzsD-xbYDZCQw6cZEDH8pS-8XTMgZKTwzHIMryg4m-wbPUTpaU8YgVyZbc46cn_P_30EE1pnKOOLr3bq9GCbrHntMUgthH2WxnLcwcz4zqlKbAk65bAlrrb7vIR-NWChrhnPeuvZZoPZjIhz8dvyQ/s320/WhatsApp%20Image%202024-02-16%20at%2009.17.23.jpeg" width="180" /></a></div><div><br /></div></div><div><br /></div><div><br /></div><div><b>B - Absolutely loved these in the past but lately I've stopped enjoying them as intense as before</b></div><div><ul><li>99% Invisible - A3, I2, O5</li><li>This American Life - A4, I3, O5</li><li>Revisionist History - A3, I3, O5</li></ul><div>The style of Revisionist History get tiring a bit too quickly. 99% Invisible and TAL have become a bit too woke for my tastes.</div><div><br /></div><div><b>C - Pretty enjoyable but too infantilized</b></div><ul><li>Cautionary Tales - A2, I3, O3</li><li>50 Things That Made the Modern World - A3, I5, O5</li></ul></div></div><p></p><p><br /></p>Jouke Walesonhttp://www.blogger.com/profile/12215646682936082041noreply@blogger.com0tag:blogger.com,1999:blog-4710497652927856789.post-58739272043407417852024-03-03T17:36:00.006+01:002024-03-14T21:25:34.390+01:00The Fog of War, or: when being vague is useful<div style="text-align: left;">I've always been a straight shooter. Perhaps it's the Dutch culture or the protestant roots, but mystery, rituals and concepts annoy me. If a company can't say clearly what their product does, I am annoyed. I don't need you to tell me that my team will be 5% more effective, let me figure out myself how useful your product is on the merits of what it actually does.</div><div style="text-align: left;"><br /></div><div style="text-align: left;">Recently I had the pleasure to work with a CEO and got some feedback that I shared plans with our suppliers too clearly. He said he didn't like that at all and he wanted things to be as vague as possible. My gut reaction was that this was ridiculous. If they don't understand our plans and motives how can they deliver a good service? It goes against everything I've read about leading people.</div><div style="text-align: left;"><br /></div><div style="text-align: left;">But, when I reflected a bit, I though of the idiom "Knowledge is Power".</div><div style="text-align: left;"><br /></div><div style="text-align: left;">If knowledge is power, then the absence of knowledge must be weakness. By keeping the people around you weak, you keep the upper hand. </div><div style="text-align: left;"><br /></div><div style="text-align: left;">I don't <i>know</i> which way is right when dealing with your own team or with suppliers (although I know which I personally prefer). But: the insight has opened my mind and I now see the pattern of hiding information all the time, so I am really grateful to the CEO for teaching me this lesson. It gave me a new mental tool to understand how the world works.</div><div style="text-align: left;"><ul style="text-align: left;"><li>In strategy games, the Fog of War is crucial. You can't see what your enemies are doing and they can attack you from any corner of the map, until you put up scouts, watchtowers, etc.<br /><br /></li><li>By not sharing salary ranges, companies keep power to compensate people how they wish. If they are asking you to give your desired salary, you lose power by giving it.<br /><br /></li><li>In a consultancy company you have no one-size-fits-all product so you might want to have a vague website saying things about "digital transformation" etc. It sounds very important without sharing any information!<br /><br /></li><li>By not publicly committing to a roadmap, your company keeps the power to change its mind when it wants to. On the other hand, some customers might want to see whether your roadmap aligns with their needs and you might not close the deal without sharing a roadmap.</li><div><br /></div><li>If you lead ambitious people and you want to keep your cozy management position, you might want to hide information from your team to prevent them from being promoted. On the other hand, <b>if your team is stable</b>, your team's power is the sum of the power of all the members. By giving your team the full information available, you can create amazingly powerful teams that do the right things the right way.<br /><br /></li><li>By not sharing plans with a supplier they might suspect you have other options, and they might work harder to get the contract.<br /><br /></li><li>By setting clear objectives for people and linking them to bonuses, promotions and salary increases, you give your people a LOT of power. If you can't set the right goals this will mess up your organization. By keeping these things vague you keep the power with management. This is often the right thing to do, because setting the right goals for a year up front is downright impossible.<br /><br /></li><li>In project planning, there is a 5d chess interplay of estimations, budgets, deadlines, scope. Think about the role of information the next time you are trying to figure out what is going on.<br /><br /></li></ul></div><div style="text-align: left;">In stable, high-trust, mission-oriented organizations power is not so important and knowledge is shared freely. In highly political organizations with lots of infighting, power is everything and information is guarded closely.</div><div style="text-align: left;"><br /></div><div style="text-align: left;">Knowledge is indeed power.</div>Jouke Walesonhttp://www.blogger.com/profile/12215646682936082041noreply@blogger.com0tag:blogger.com,1999:blog-4710497652927856789.post-31949184311305735432024-03-03T12:52:00.001+01:002024-03-03T13:05:32.870+01:00The unreasonable effectiveness of i3, or: ten years of a boring desktop environment<p>My wife uses Windows and over the years I've helped her move things to new systems. Win8, 10 and now 11. With every change the UI changes. Now I can't right click the bottom right corner anymore to open Task Manager. The UI feels "fresh" and up-to-date I guess, but does it really matter?</p><p>My desktop has looked like this since 2008. I love the picture of the gearbox, it's a testament to the hidden precision engineering that goes on inside the built world all the time.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjIWAooPMyhxmdRJnXw77d_r2eyEeh3VD8EFh2SN2bQTjUqt__xk0MzilABTDLzJU2ugzPnANC2Pig3_KK7_UYw8LUxMKzxHg2jm1-SnYVE_gz4LDh6LqlZa-R-J1cBBOP_tKwb89CTeHzW4nwc4ufBSAHKXg-ON6RfUW7bwB-tpTM5NOJjgZlHB4HvpPw/s1920/Screenshot%20from%202024-03-03%2012-40-08.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1080" data-original-width="1920" height="180" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjIWAooPMyhxmdRJnXw77d_r2eyEeh3VD8EFh2SN2bQTjUqt__xk0MzilABTDLzJU2ugzPnANC2Pig3_KK7_UYw8LUxMKzxHg2jm1-SnYVE_gz4LDh6LqlZa-R-J1cBBOP_tKwb89CTeHzW4nwc4ufBSAHKXg-ON6RfUW7bwB-tpTM5NOJjgZlHB4HvpPw/s320/Screenshot%20from%202024-03-03%2012-40-08.png" width="320" /></a></div><p>I don't see much of the gearbox though, because most of the time my screens look like this:</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhtJoAHz-i5rOKKpLpyGF4K1NGy69kJCo5TBFAeMwzLb_g5ERaYBHInpg7hPvd2OXreJ_A5QBTXxeLpaRb5Jg9IURgudjVk8Dk07nuI-ZN2Hx9zqakuTbgT1LzGPWmPBEfcN0SArt7zBAQsISr-nF8ptxbF6puYR8gvZPy_dy2hAq2DTGlOiozawb-ViLI/s1920/Screenshot%20from%202024-03-03%2012-50-19.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1080" data-original-width="1920" height="180" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhtJoAHz-i5rOKKpLpyGF4K1NGy69kJCo5TBFAeMwzLb_g5ERaYBHInpg7hPvd2OXreJ_A5QBTXxeLpaRb5Jg9IURgudjVk8Dk07nuI-ZN2Hx9zqakuTbgT1LzGPWmPBEfcN0SArt7zBAQsISr-nF8ptxbF6puYR8gvZPy_dy2hAq2DTGlOiozawb-ViLI/s320/Screenshot%20from%202024-03-03%2012-50-19.png" width="320" /></a></div><p>The environment around my background picture has changed a little more, but has been stable for the last 10 years. My experience is very different to my wife's constantly changing system. In 2009 I moved from Windows to Ubuntu, then to Debian using Gnome. Then finally to i3 in 2013 after a brief affair with XMonad in 2012. So by now in early 2024, I've had the same minimal UI for more than 10 years, and if I'd only had the guts to adopt i3 earlier it could have been 15. I have made small changes but if I didn't like it I would revert back to the previous version. All the changes were conscious efforts and I had the freedom to go back when I wanted to.</p><p>This short post is in praise to boring stable systems, and a thank you to all the open source contributors that made my desktop environment possible.</p><p></p><ul style="text-align: left;"><li>OS: Debian (since 2011)</li><li>Window manager: i3 (since 2013)</li><li>Browser: Chrome (since 2008)</li><li>Editor: vim (since 2011, now actually neovim but I haven't noticed a difference with vim)</li><li>Terminal: gnome-terminal (since 2009)</li><li>Shell: zsh / oh-my-zsh (since 2014)</li><li>Mail: Gmail (since 2005)</li></ul><div>Here are some new improvements to my home computer systems.</div><ul style="text-align: left;"><li>Backup management: syncthing (since 2022)</li><li>Home automation and energy management: home-assistant (since 2022)</li><li>Microcontroller management: espthing (since 2024)</li><li>Shell history management: atuin (trying it out since early 2024)</li></ul><div>Being boring with tools is great. It gives you the freedom to be revolutionary in areas where you actually want to make a difference.</div><p></p>Jouke Walesonhttp://www.blogger.com/profile/12215646682936082041noreply@blogger.com2tag:blogger.com,1999:blog-4710497652927856789.post-22528038624801712882024-01-08T21:33:00.001+01:002024-01-08T21:33:22.886+01:00Aiyima remote control codes<p>I "upgraded" my old Technics SU-VX700 amplifier a couple weeks ago. After 15 years of loyal service I wanted to switch to something that had digital inputs, was more energy efficient, and had a remote control. After some digging I found the Aiyima D03. It checked all the boxes + was tiny, had bluetooth and was and only 150 euros. I call it "upgraded" because the Technics was a beast of a Class AA (whatever that means) amplifier and probably very Hifi worthy, the D03 might be considered a downgrade by audiophiles, but I'm not sophisticated enough to be able to tell the difference. Mind you, I am sophisticated enough to be very annoyed by TVs without external speakers. I think both amps sound great on my Linn Keilidh speakers.</p><p>The remote control was nice, but it's a separate one from my tv. Using the optical toslink output the volume buttons on my TV do nothing. Annoying for me and my family and very confusing for guests who wonder why the TV has no sound.</p><p>I started using some esphome microcontrollers for various home automation tasks, and I got an idea. Why don't I listen on IR to my TV remote and if the volume buttons are pressed, mimic the Aiyima remote and send the volume signals there too. I actually ordered some IR Transmitter and IR Receiver break-out boards some time ago but never used them before.</p><p>On a Saturday night I hooked everything up and started debugging the signals from the Philips and Aiyima remote controls. I took a quick look with my oscilloscope but then found how to come to a conclusion quicker using <a href="https://community.home-assistant.io/t/faking-an-ir-remote-control-using-esphome/369071" target="_blank">this blog</a>.</p><p>The Philips one was easy enough and showed up right away. The Aiyima uses some custom prefix and codes, or at least one not known to the esphome libraries. Many manufacturers use overlapping protocols (even though the keys might not overlap, not all the address space is used) so it recognized JVC, Pronto, Pioneer and some other codes.</p><p>Through trial and error I got it to work by using transmit_pioneer 0x4d80 through 0x4dff.</p><p>So if you ever want to accomplish the same, here are all the buttons on the remote mapped out.</p><p><br /></p><p>Action - #nr - pioneer equivalent hex code</p><pre>bas- 0 0x4d80
tre- 1 0x4d81
mode 2 0x4d82
bas+ 8 0x4d88
previous 25 0x4d99
next 65 0x4dc1
tre+ 69 0x4dc5
vol+ 74 0x4dca
mute 78 0x4dce
vol- 82 0x4dd2
off 92 0x4ddc</pre><p>Having examined how similar the different protocols are, I feel like there might be a better way of doing this than emulating pioneer but I'm happy with this solution for now.</p><p>I was hoping that there would be secret codes on the amp that were not present on the remote control, to idempotently switch off the amp, switch to a specific input or go to a pre-set volume. That would be great for some more automation. I tried all 256 codes from 0x4d00 to 0x4dff but did not get any results. It's possible that an idempotent "switch on" is present but I didn't want to try all codes twice. I thought of Shenzen IO and realized that of course no one had time for this. That's what I deserve for always saying YAGNI to my engineers.</p><p>If you want to plug this into esphome, you can do it like so. This contains just one action but the rest can be extrapolated from the table above:</p><div><pre>button:
- platform: template
name: amp mute toggle
on_press:
- remote_transmitter.transmit_pioneer:
rc_code_1: 0x4dce # amp mute tuggle
</pre><div><br /></div></div>Jouke Walesonhttp://www.blogger.com/profile/12215646682936082041noreply@blogger.com0tag:blogger.com,1999:blog-4710497652927856789.post-51747560974866385772023-12-10T16:04:00.020+01:002024-01-04T09:24:11.674+01:00Cowboy C3 battery teardown / disassembly and rear light fix<p>A bit over a week ago the rear light of my Cowboy v3 / C3 broke down. Everything else was fine.</p><p>Update on January 4th 2024: In the end I managed to fix the light. Check the bottom of the post for the conclusion. Thanks to reddit user <i>icannotfindagoodname</i> for the help in finding the issue!</p><p>After chatting with Cowboy support they offered me a new battery for 490 euros and that they would also send an offer with a discount, which appeared a couple days later. The discounted price was 367 euros. A bit much for a broken LED module! The battery was still totally fine.</p><p>So I'll attempt to fix it myself and I'll show you how to disassemble the battery here.</p><p>WARNING - DO NOT DO THIS AT HOME</p><p>Now I need to warn you to not try this at home. I have been working with electronics for a long long time and I know what to do and what not to do, and most importantly, know what I should not even try. Battery packs are not a joke and could lead to serious fire and health problems if not handled correctly. Real professionals will probably even look at my photos and say that I'm an idiot for doing this in my living room. I do not assume any responsibility for anything you do in your life, including opening up dangerous lithium ion battery packs.</p><p>WARNING - DO NOT DO THIS AT HOME</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhFIK0ieFsgq-af8MKar7uLPIkgSAdSeCZ1_rblFf2FIpuE0Gg61ZXgk1KnIBBsOYPwmvvHZGzyGphbSkLsVajklTpWpF0lIjULZoOdZI5cIGI7hfBYFNEC4IgHt2o6FcTzl79sAF0mfOvq7v9QKXWSAZozNlVwoJnXon0YsgTFcKHcPXT4pUr1n2azO7g/s4032/IMG_8613.JPG" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="4032" data-original-width="3024" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhFIK0ieFsgq-af8MKar7uLPIkgSAdSeCZ1_rblFf2FIpuE0Gg61ZXgk1KnIBBsOYPwmvvHZGzyGphbSkLsVajklTpWpF0lIjULZoOdZI5cIGI7hfBYFNEC4IgHt2o6FcTzl79sAF0mfOvq7v9QKXWSAZozNlVwoJnXon0YsgTFcKHcPXT4pUr1n2azO7g/s320/IMG_8613.JPG" width="240" /></a></div><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both;">We start with the 4 screws at the bottom.</div></div><div class="separator" style="clear: both; text-align: center;"><br /></div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgwaOyVpUK42agX8ya_uOTxgH8c46ynO2R-CeinQ3YmI0MFG1WhAYew4BZsaEFs1TT0_EHAz5VTivrSG79Ygk5ai_NImN9QQ_SJio2XGn4xyx4X5vMOcc_6NIFw-scuUIZ9-tmPbokJUV1YaVuzuQWlRTF_3vO51KrUpRDzWf_yGl3OUxcqtY7Tc1C4whA/s4032/IMG_8615.JPG" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="4032" data-original-width="3024" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgwaOyVpUK42agX8ya_uOTxgH8c46ynO2R-CeinQ3YmI0MFG1WhAYew4BZsaEFs1TT0_EHAz5VTivrSG79Ygk5ai_NImN9QQ_SJio2XGn4xyx4X5vMOcc_6NIFw-scuUIZ9-tmPbokJUV1YaVuzuQWlRTF_3vO51KrUpRDzWf_yGl3OUxcqtY7Tc1C4whA/s320/IMG_8615.JPG" width="240" /></a></div><div class="separator" style="clear: both; text-align: center;">Great success.</div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiCFcjU5z3u7XQ4oTTqirBMvKv52XrSCSHbpZ71cVKaTxBIvY-_tAjV6fON40QVUY04Lta_Azet5fkZkfRCfd0e1UKMjtbluUCiQqt1aZwIZpLb1bvZyrgn2VzESJXNBUj46tZD6vb8jjpLQ4WQaUUH-iFgS3Sd4G3CCKHHdSMLCd1T7aanxLtihhoctTw/s4032/IMG_8616.JPG" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="4032" data-original-width="3024" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiCFcjU5z3u7XQ4oTTqirBMvKv52XrSCSHbpZ71cVKaTxBIvY-_tAjV6fON40QVUY04Lta_Azet5fkZkfRCfd0e1UKMjtbluUCiQqt1aZwIZpLb1bvZyrgn2VzESJXNBUj46tZD6vb8jjpLQ4WQaUUH-iFgS3Sd4G3CCKHHdSMLCd1T7aanxLtihhoctTw/s320/IMG_8616.JPG" width="240" /></a></div><div class="separator" style="clear: both; text-align: center;">Then remove these 4 screws here.</div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgLUDzepoQjwaZYsmLlN4NnLBy-jsJybTwegwF6MhKfdAsnRY_nmSoFWz2XSMuAWfwboUsZqtG4Og7tVuprDHLIZPAnDfIlhXLfvFT54mg1UcKI2dc6-7EARezTzbz09yf6vp-2QR7-P6xjHseJrioxP5FFGPnrwyM35uF2RJpHETqJ3qFJZlj9fx1q_rA/s4032/IMG_8619.JPG" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="4032" data-original-width="3024" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgLUDzepoQjwaZYsmLlN4NnLBy-jsJybTwegwF6MhKfdAsnRY_nmSoFWz2XSMuAWfwboUsZqtG4Og7tVuprDHLIZPAnDfIlhXLfvFT54mg1UcKI2dc6-7EARezTzbz09yf6vp-2QR7-P6xjHseJrioxP5FFGPnrwyM35uF2RJpHETqJ3qFJZlj9fx1q_rA/s320/IMG_8619.JPG" width="240" /></a></div><div class="separator" style="clear: both; text-align: center;">Here it gets complicated. There's only a tiny hole through which to operate.</div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj-PW0GANkEjpL4l5IvYWncOMSMEDTt372Sp8nrVLcmdwAvlA4Z8gINxw48RXGHoiwni6eNOdVwvZZEBkwy4Cyh61Ak0fciuvWnNW_4NrcV37xs897L8Kd5rRQzIayHCz9JFj5wm1HCLYafa3zthoc4KYy90p3Pd4kwnD8T3Fd-3J0_9SEl62WkPD3I3NY/s4032/IMG_8623.JPG" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="4032" data-original-width="3024" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj-PW0GANkEjpL4l5IvYWncOMSMEDTt372Sp8nrVLcmdwAvlA4Z8gINxw48RXGHoiwni6eNOdVwvZZEBkwy4Cyh61Ak0fciuvWnNW_4NrcV37xs897L8Kd5rRQzIayHCz9JFj5wm1HCLYafa3zthoc4KYy90p3Pd4kwnD8T3Fd-3J0_9SEl62WkPD3I3NY/s320/IMG_8623.JPG" width="240" /></a></div><div class="separator" style="clear: both; text-align: center;">I also pried open the top thinking that there would be easy access to the PCB connectors, but no. It's just the cylinder lock. The two compartments are totally isolated. No need to open this up unless there's any problem with your lock. Coincidentally the tiny hole that looks like a "insert paperclip to reset CMOS" hole is just a water(?) drain. There's no reset button behind it.</div><div class="separator" style="clear: both; text-align: center;"><br /></div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiEr5RYT0TIbri9-QnAtV5ks4pEBNvWikSq973dGoVfWNm1K-8l1RNTfwPSTBTzI7_-fBep-vtI7jBD76EWdAzdGGHL6QZzMOZ8nfXW4F8kLY7PlYNpXbIlrdbCxyITzEKMiJm3R4NkLHaWHV5pucjiAhzm-zLA6fmI-jgjaOG0PstFmCdx20gYGsS6K84/s4032/IMG_8628.JPG" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="4032" data-original-width="3024" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiEr5RYT0TIbri9-QnAtV5ks4pEBNvWikSq973dGoVfWNm1K-8l1RNTfwPSTBTzI7_-fBep-vtI7jBD76EWdAzdGGHL6QZzMOZ8nfXW4F8kLY7PlYNpXbIlrdbCxyITzEKMiJm3R4NkLHaWHV5pucjiAhzm-zLA6fmI-jgjaOG0PstFmCdx20gYGsS6K84/s320/IMG_8628.JPG" width="240" /></a></div><div class="separator" style="clear: both; text-align: center;">I also removed the long screw, which you DO NOT NEED TO DO. It's just for the structural integrity of the battery pack and is not attached to the frame.</div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjhyC02t5jd4zELxv3v2IK9bXgTAM2z3H6U3iSy2bPZC193zAo8LDXdR2H3J4tCaMMjepeLWOrVaILZodR3qb8AnuLJ0zZSoalqVSVdQj0HnD3woHiEPMPCJN3Xs6jOPlYgqDYUBxMJn2kor0kM0uxmi3yZLUwYAAEOVKM91gIqTYhD4BHiQ8WgHQWczs0/s4032/IMG_8630.JPG" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="4032" data-original-width="3024" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjhyC02t5jd4zELxv3v2IK9bXgTAM2z3H6U3iSy2bPZC193zAo8LDXdR2H3J4tCaMMjepeLWOrVaILZodR3qb8AnuLJ0zZSoalqVSVdQj0HnD3woHiEPMPCJN3Xs6jOPlYgqDYUBxMJn2kor0kM0uxmi3yZLUwYAAEOVKM91gIqTYhD4BHiQ8WgHQWczs0/s320/IMG_8630.JPG" width="240" /></a></div><div class="separator" style="clear: both; text-align: center;"><br />After a lot of prying through the tiny hole I managed to disconnect the connectors and remove the outside interface. This was the hardest part of the disassembly! It's really difficult to operate through a tiny hole and still be careful because you're near the main output terminals of the battery pack.</div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgLw2QfGl6Y0KHaA4wTBUjvXnzGcoUwQMrYSJ7APlzWD_KnNpKOP3_5Uv3I4GdYFNRgcJxgFi-jIksTi2on-x1-KUCoG6KGZS2XMUgeTEDvqNIv_uUt6HROLJdCajItIJ-wf5tNTbhqCQJOo7Mi8iZSWs-BAon6zo5jDBEwkDBbNxEpUuRn5u72E4pRrCM/s4032/IMG_8632.JPG" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="4032" data-original-width="3024" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgLw2QfGl6Y0KHaA4wTBUjvXnzGcoUwQMrYSJ7APlzWD_KnNpKOP3_5Uv3I4GdYFNRgcJxgFi-jIksTi2on-x1-KUCoG6KGZS2XMUgeTEDvqNIv_uUt6HROLJdCajItIJ-wf5tNTbhqCQJOo7Mi8iZSWs-BAon6zo5jDBEwkDBbNxEpUuRn5u72E4pRrCM/s320/IMG_8632.JPG" width="240" /></a></div><div class="separator" style="clear: both; text-align: center;">Now only the light is still attached. If you push REAL HARD through the hole you can eject the rear light which is glued down. Then gently remove the connector for the light. I didn't figure out how it worked before so I slightly damaged some plastic of the lights. All in the name of science I guess.</div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg8qsicPw4uQvBXNKVl1u-OQ83EGv6XpBE10ZchvJei5B_zXAwGfsf7iaP-g_bpT66JUHLWRvbqRg79pvXIhza9DWOLRze5c6Aqmn4jTQGjpjod_-012l9LKBq3PSQNc6C9qgU8ZLDoKPKXDnWpVxtUcDYaVaoKzgTRKU72Ye4fpgSZhYQKxANo-MZoYMo/s4032/IMG_8631.JPG" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="4032" data-original-width="3024" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg8qsicPw4uQvBXNKVl1u-OQ83EGv6XpBE10ZchvJei5B_zXAwGfsf7iaP-g_bpT66JUHLWRvbqRg79pvXIhza9DWOLRze5c6Aqmn4jTQGjpjod_-012l9LKBq3PSQNc6C9qgU8ZLDoKPKXDnWpVxtUcDYaVaoKzgTRKU72Ye4fpgSZhYQKxANo-MZoYMo/s320/IMG_8631.JPG" width="240" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: center;">Ok now how do you get the battery pack out of the enclosure? The fit is very tight and there's no surface to apply pressure to. You can't push from the inside and you definitely DO NOT want to stick metal tools anywhere near the cells. The side with the PCB has the most clearance from the edge of the frame. I figured that by slamming the frame down onto a hard edge (like the side of a workbench), the momentum of the cells will do the work and slowly slide out. And after 4 or 5 "gentle" slams the pack was indeed ejected.</div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: center;">Here are some shots of the pack and the PCB.</div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: center;">The white "paste" covers the leads that go to the batteries. I'm not replacing any cells so I'm not touching it.</div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjbKoxHUCYFf_nWQ_HhOkb93C9Ti-uwDVpYw3xZDfqVYmXy1x7OcQ3CODq0TgNX5qS3QpIjtVUwznuP9Q6HL5W3GndZrQAxXp6MT9oh1uRq4rdrzkPbJiHZeYRL9r6B7Pa-FsPqnQV8RWtAEdvYU5hjI_bPl-aqJ62_Uvwx7UyFNHi0_ZuOgcYg7fXaJ78/s4032/IMG_8678.JPG" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="4032" data-original-width="3024" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjbKoxHUCYFf_nWQ_HhOkb93C9Ti-uwDVpYw3xZDfqVYmXy1x7OcQ3CODq0TgNX5qS3QpIjtVUwznuP9Q6HL5W3GndZrQAxXp6MT9oh1uRq4rdrzkPbJiHZeYRL9r6B7Pa-FsPqnQV8RWtAEdvYU5hjI_bPl-aqJ62_Uvwx7UyFNHi0_ZuOgcYg7fXaJ78/s320/IMG_8678.JPG" width="240" /></a></div><div class="separator" style="clear: both; text-align: center;">There's a small push button here. Not sure what it's for.</div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjBhhGFY1-T5ffd3Wq5wq_onT6R0MtxxBO_2HG1e1U6g6dXO6636f18ePTLnUXcxpzZ5l43qmM6yDdgyofPzkGTeV6kcfV80sB5ZU2a-JrSLIun9T8UxPMIhwcolOvK5N_6PefdMe8x9lrGvvBvMza3ojxEzWFXGVDAl_aZJFhSTGARxYU9wAStgMeEAL0/s4032/IMG_8677.JPG" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="4032" data-original-width="3024" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjBhhGFY1-T5ffd3Wq5wq_onT6R0MtxxBO_2HG1e1U6g6dXO6636f18ePTLnUXcxpzZ5l43qmM6yDdgyofPzkGTeV6kcfV80sB5ZU2a-JrSLIun9T8UxPMIhwcolOvK5N_6PefdMe8x9lrGvvBvMza3ojxEzWFXGVDAl_aZJFhSTGARxYU9wAStgMeEAL0/s320/IMG_8677.JPG" width="240" /></a></div><div class="separator" style="clear: both; text-align: center;">Also an on/off switch for the entire pack.</div><br /><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjBmUmNPMa7SOHnuEUpuWu30SvwJCdjonnTHTNVOSI6NePNEha6_jujcCOEV49TN8l_pXywQoMg6hfOK8G9Cez54OBwQ7Evzf-HogpRb1yRdav9SH-XTIHl-pX1Kthm_dieDwthtButCSNfD9c0h8RMdJ3wUbEHfLj7mbVL_IOO-p0jH3QaMKh4MqafeWQ/s4032/IMG_8634.JPG" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="4032" data-original-width="3024" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjBmUmNPMa7SOHnuEUpuWu30SvwJCdjonnTHTNVOSI6NePNEha6_jujcCOEV49TN8l_pXywQoMg6hfOK8G9Cez54OBwQ7Evzf-HogpRb1yRdav9SH-XTIHl-pX1Kthm_dieDwthtButCSNfD9c0h8RMdJ3wUbEHfLj7mbVL_IOO-p0jH3QaMKh4MqafeWQ/s320/IMG_8634.JPG" width="240" /></a></div><div class="separator" style="clear: both; text-align: center;">Here's the pack.</div><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjzsGuEw1T4L5D8Brp2yGsjeFgUukLWCysnA_AqT6Y8TC7-85qCIP_ULwObn8BnGHKE92hovQP7aVI7RInD8yI000Ej-7stjM4Lm4fgSosrOjpDRNT06PuPZ-vU4KbxGq0ULtFa4WQbQXo7Ujgi5_nlFONaKBT-EF9iRUoJpHyuuULh4n17asUTb9-aPhc/s4032/IMG_8640.JPG" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="4032" data-original-width="3024" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjzsGuEw1T4L5D8Brp2yGsjeFgUukLWCysnA_AqT6Y8TC7-85qCIP_ULwObn8BnGHKE92hovQP7aVI7RInD8yI000Ej-7stjM4Lm4fgSosrOjpDRNT06PuPZ-vU4KbxGq0ULtFa4WQbQXo7Ujgi5_nlFONaKBT-EF9iRUoJpHyuuULh4n17asUTb9-aPhc/s320/IMG_8640.JPG" width="240" /></a></div><div class="separator" style="clear: both; text-align: center;">This is where it's attached to the rear lights.</div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEheSCnu4dVQKJlovutwvs_TxTfx1H_I6LG_L1emo4nlUkH7Tcyh9ChH-caVwUmbxrLRrZ0ItFgaM5_cpuQZwuYQds-GxlTTmwEa_pNbi3VRtmTWF4opGF7qOTje94JhkyZkQeOM8hbm4GhkQ1C6r3N7xtSK1NkD730nNZhkhqF56KcRkacCuPxBqVpzuNY/s4032/IMG_8641.JPG" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="3024" data-original-width="4032" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEheSCnu4dVQKJlovutwvs_TxTfx1H_I6LG_L1emo4nlUkH7Tcyh9ChH-caVwUmbxrLRrZ0ItFgaM5_cpuQZwuYQds-GxlTTmwEa_pNbi3VRtmTWF4opGF7qOTje94JhkyZkQeOM8hbm4GhkQ1C6r3N7xtSK1NkD730nNZhkhqF56KcRkacCuPxBqVpzuNY/s320/IMG_8641.JPG" width="320" /></a></div><div class="separator" style="clear: both; text-align: center;">And here next to the enclosure.</div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: center;">Ok now we get to what we came here for... <div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjy5UNdA8r7Q_vyt8ifnxBHuNUeWOUC9Z1z1gzrhb03sTIgTi0zc5pROfB0FGqHwyCEGwoArLmWqgeAbre1Wmac8Oz0m_wzDXWDfQYyuIPtmTk2tdY9DmI_TFvLXAngjiuDo-DMfwpF4qtAEYONTM3kr-hbMECJUV8zAWLsBnRlleeVeuiEKqX6KI4vHAg/s4032/IMG_8644.JPG" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="3024" data-original-width="4032" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjy5UNdA8r7Q_vyt8ifnxBHuNUeWOUC9Z1z1gzrhb03sTIgTi0zc5pROfB0FGqHwyCEGwoArLmWqgeAbre1Wmac8Oz0m_wzDXWDfQYyuIPtmTk2tdY9DmI_TFvLXAngjiuDo-DMfwpF4qtAEYONTM3kr-hbMECJUV8zAWLsBnRlleeVeuiEKqX6KI4vHAg/s320/IMG_8644.JPG" width="320" /></a></div><div class="separator" style="clear: both; text-align: center;">The rear light module in all its (broken) glory.</div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhuPGX4t5l6WUEjIpK1wDno6c3olUlGXcatXlbcS-i_u3zQbvlqE7t1X_MX0wkJYOubdFISasFnky9ghSlVjXTeQLytMLrt1cStj6MYP4cAd0gyvK0-LN575uVNdp3abZdeHZ_CwtW1yOBJeYnLhMyYa5hdGbMLTp6wqR7zet4bBWWfgIjFHLgOstpmz-M/s4032/IMG_8645.JPG" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="4032" data-original-width="3024" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhuPGX4t5l6WUEjIpK1wDno6c3olUlGXcatXlbcS-i_u3zQbvlqE7t1X_MX0wkJYOubdFISasFnky9ghSlVjXTeQLytMLrt1cStj6MYP4cAd0gyvK0-LN575uVNdp3abZdeHZ_CwtW1yOBJeYnLhMyYa5hdGbMLTp6wqR7zet4bBWWfgIjFHLgOstpmz-M/s320/IMG_8645.JPG" width="240" /></a></div></div>It's easily pried open.</div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEha1Upq0YhRel_cO9OjkHRFSwQ9r_0OhlvCVasRALGbvy4PyyoJbzSg4wD_XeI5uT3hP7nAZbKJ1qz2X5exCKUgw1w96vMlDAXXAMx9r03J1lzSl8HoNVJ-zx8SNvB2EbP4U7h26vKwXJS0U0WyJ-M4k41NgQF1Fo7P7ljPHrM5C3WpH86-zvnMVRsqbEE/s4032/IMG_8646.JPG" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="4032" data-original-width="3024" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEha1Upq0YhRel_cO9OjkHRFSwQ9r_0OhlvCVasRALGbvy4PyyoJbzSg4wD_XeI5uT3hP7nAZbKJ1qz2X5exCKUgw1w96vMlDAXXAMx9r03J1lzSl8HoNVJ-zx8SNvB2EbP4U7h26vKwXJS0U0WyJ-M4k41NgQF1Fo7P7ljPHrM5C3WpH86-zvnMVRsqbEE/s320/IMG_8646.JPG" width="240" /></a></div><div class="separator" style="clear: both; text-align: center;">Alright so here we just have 8 LEDs. It seems the voltage is about 1.5V tot 2V per LED.</div><div class="separator" style="clear: both; text-align: center;"><br /><br /></div><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhXlg99GCnMNwhzwUxqTB5HL6fmdaNrmMavFMLrvNixZEqvtGfEHgdNdJx5pV6B3lj-G7nDBx_qXlH2yOeSaxUozAeJopB-ccGlMqJDGA9szNaG9lK3vHW-W5euPkvURtE3rR_GWL84zePkodMnuv0vuUek9bclPLOaboLTaEz_T6RXegu2i0knx6Vl9kE/s4032/IMG_8667.JPG" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="4032" data-original-width="3024" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhXlg99GCnMNwhzwUxqTB5HL6fmdaNrmMavFMLrvNixZEqvtGfEHgdNdJx5pV6B3lj-G7nDBx_qXlH2yOeSaxUozAeJopB-ccGlMqJDGA9szNaG9lK3vHW-W5euPkvURtE3rR_GWL84zePkodMnuv0vuUek9bclPLOaboLTaEz_T6RXegu2i0knx6Vl9kE/s320/IMG_8667.JPG" width="240" /></a></div><div class="separator" style="clear: both; text-align: center;">Now I tested my LEDs and they all still work. Hm.. Could be the rear light module or the signal from from the bike's controller.</div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiUU9DJ_woGwW_PDiDopgNbmU6lEBNUNedFBOjnrotf8yKsr0mLgThJR7NrMAPbPnhJhj-FsiywFesGAeiUfEVEgKLlGuZO-Yks70VZiQ8HI9rVsD8rxJqpoI1inv7qnKNbMtuJWLdTo7D0ZvuJGgrNn7Xy_jhjX6ty9MX_pGIuw8Cqg8ld8h-IwBshsBQ/s4032/IMG_8682.JPG" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="3024" data-original-width="4032" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiUU9DJ_woGwW_PDiDopgNbmU6lEBNUNedFBOjnrotf8yKsr0mLgThJR7NrMAPbPnhJhj-FsiywFesGAeiUfEVEgKLlGuZO-Yks70VZiQ8HI9rVsD8rxJqpoI1inv7qnKNbMtuJWLdTo7D0ZvuJGgrNn7Xy_jhjX6ty9MX_pGIuw8Cqg8ld8h-IwBshsBQ/s320/IMG_8682.JPG" width="320" /></a></div><br /></div><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both;">The red and black terminals of the LED module are hooked directly to the main battery terminals, so there's some power modulation from 36V down to a usable level for the LEDs. The green wire is controlled from the main PCB in the frame of the bike, the connection goes through one of the small terminals of the battery connector and then directly to the led module. The signal is a 3V PWM at 1khz. So it's probably the LED module that's broken. This is actually the first time I'm seriously using my oscilloscope and it was awesome!</div><div class="separator" style="clear: both;"><br /></div></div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: left;">The LEDs are wired in serial. When lighting up the first or last 7 of them with 12V it works, but when connecting all 8 of the LEDs I got a short circuit, which means that there's something wrong with my LED PCB. I suspect it's the power inductor that overheated and will try to fix it.</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;">Here are some close-ups of the LED PCB.</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhOJ9bVyvDUCeP4MfaiA99e-JadLrKru-2SMBom3LCocyINaM50i6Ey-LDompKN4daA2aSvl57H8kpBeMeUzaweSGYODHrDerslFcn1IK3IZB8ADp4JmNZ_4ngjLAWaeq4Yf7aR2npp-ddhhrOnG6_n6NZwe_mcnUedVU32ylb15dghnfON746sSeP8va4" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="2848" data-original-width="4288" height="213" src="https://blogger.googleusercontent.com/img/a/AVvXsEhOJ9bVyvDUCeP4MfaiA99e-JadLrKru-2SMBom3LCocyINaM50i6Ey-LDompKN4daA2aSvl57H8kpBeMeUzaweSGYODHrDerslFcn1IK3IZB8ADp4JmNZ_4ngjLAWaeq4Yf7aR2npp-ddhhrOnG6_n6NZwe_mcnUedVU32ylb15dghnfON746sSeP8va4" width="320" /></a></div><br /><br /></div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjWGyzPoNH8CRgQHkeAO2uL1s8wMfMEKcBkMZVIN0Z1jtMU0GGGBghRuaebRvtQyCQIPo7bZiAX5d65cNcCzpUvFVoH0FJl9B7qQn1I_tQzCc4TihFNiyiSodyAOT7wjC48KtCtXaP4aJXs2jT3RpmBT8ve_JZKA_tSxlHvjK1X_IA-_5774HCWMgouc-E/s1920/PICT0005.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1440" data-original-width="1920" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjWGyzPoNH8CRgQHkeAO2uL1s8wMfMEKcBkMZVIN0Z1jtMU0GGGBghRuaebRvtQyCQIPo7bZiAX5d65cNcCzpUvFVoH0FJl9B7qQn1I_tQzCc4TihFNiyiSodyAOT7wjC48KtCtXaP4aJXs2jT3RpmBT8ve_JZKA_tSxlHvjK1X_IA-_5774HCWMgouc-E/s320/PICT0005.jpg" width="320" /></a></div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg_CjGNYnnLRvqY0pLlGw1G7-iaX29urqkhXKBwz_w21Eet8OgdtfgSZMno9A50lbQska1Ekdb9KAyD2iO_6i9H0faegREBmXmiQKrIM34S5eS_wPrL3pAWSzVyEdvPz3yUaJXXZmwggC6TMD-lND9_O1JzHFXomDsV3tGgrh2tXFWbklLJjqE93lhiKg0/s1920/PICT0006.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1440" data-original-width="1920" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg_CjGNYnnLRvqY0pLlGw1G7-iaX29urqkhXKBwz_w21Eet8OgdtfgSZMno9A50lbQska1Ekdb9KAyD2iO_6i9H0faegREBmXmiQKrIM34S5eS_wPrL3pAWSzVyEdvPz3yUaJXXZmwggC6TMD-lND9_O1JzHFXomDsV3tGgrh2tXFWbklLJjqE93lhiKg0/s320/PICT0006.jpg" width="320" /></a></div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh0UWJdv6bHDbcidISA8ie3sIkL7qJ6N70dsJg20hOFE0U4k2PNM6cEramKcmQl542GusaczBVD-CiSRu5SyIU0oeS9RJIfJ4OPI-uWJdAZydOKo9axWUG6gRC5Lnp96OzXZnOldzEYGtOZoiWSedCyNa0S5aJwb2dSVHf3ul0pm8e2sVdqL_lRwT5IPvQ/s1920/PICT0015.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1440" data-original-width="1920" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh0UWJdv6bHDbcidISA8ie3sIkL7qJ6N70dsJg20hOFE0U4k2PNM6cEramKcmQl542GusaczBVD-CiSRu5SyIU0oeS9RJIfJ4OPI-uWJdAZydOKo9axWUG6gRC5Lnp96OzXZnOldzEYGtOZoiWSedCyNa0S5aJwb2dSVHf3ul0pm8e2sVdqL_lRwT5IPvQ/s320/PICT0015.jpg" width="320" /></a></div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiGmvQhwCs453uq4HHjDhSOQPcrWd2efNjaYPUP7DwHkJV_3u9Wij3t471QFWw-su-e7LKW-L9wzWQBSJJ3NH5SLZdqcj7cTl730vuS4u_nA0hdoeDqNkcEzK_N1KZxQhlPg1Tk5TxLIEytD2Wf-R6QsQR0Y0ckb6wsEy5AVT3uPnh6R5w9IeXXaiNA1So/s1920/PICT0017.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1440" data-original-width="1920" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiGmvQhwCs453uq4HHjDhSOQPcrWd2efNjaYPUP7DwHkJV_3u9Wij3t471QFWw-su-e7LKW-L9wzWQBSJJ3NH5SLZdqcj7cTl730vuS4u_nA0hdoeDqNkcEzK_N1KZxQhlPg1Tk5TxLIEytD2Wf-R6QsQR0Y0ckb6wsEy5AVT3uPnh6R5w9IeXXaiNA1So/s320/PICT0017.jpg" width="320" /></a></div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjxKE9asRgRQxAggQLz_RLKR-CK_redEsfUM8no8Q6gCOMGR5mIAkeNwnXaIfZydXE5L5bkUkPP85C4swARFUw3L9vPxpVrqjD1XS4lf0cyYtH7evT2czCIH-Fc4gPvpKXq01u5Tr_SMXXV81WpVK8nnE2EtZ1s5Ba_gv-BarIhvIpBdW5owKDOEA-DqHc/s1920/PICT0018.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1440" data-original-width="1920" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjxKE9asRgRQxAggQLz_RLKR-CK_redEsfUM8no8Q6gCOMGR5mIAkeNwnXaIfZydXE5L5bkUkPP85C4swARFUw3L9vPxpVrqjD1XS4lf0cyYtH7evT2czCIH-Fc4gPvpKXq01u5Tr_SMXXV81WpVK8nnE2EtZ1s5Ba_gv-BarIhvIpBdW5owKDOEA-DqHc/s320/PICT0018.jpg" width="320" /></a></div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg3HPJwz5T56iM5ab_58P-xEyJ4fkIt7DB1he6H6DtGtvJNjWKNeApMc5yAL7EUANYNSWSipTylgzPL0Q2ThpWoNB7usmodI5Ep3QadKsEgH8kfU3GySzeqKvCjU7gqYV46ufSRElCKBufySqoV3E7GNyf1wrwZnwdJo3O_2SvBw0ahGzku6zF6C7uhsq4/s1920/PICT0019.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1440" data-original-width="1920" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg3HPJwz5T56iM5ab_58P-xEyJ4fkIt7DB1he6H6DtGtvJNjWKNeApMc5yAL7EUANYNSWSipTylgzPL0Q2ThpWoNB7usmodI5Ep3QadKsEgH8kfU3GySzeqKvCjU7gqYV46ufSRElCKBufySqoV3E7GNyf1wrwZnwdJo3O_2SvBw0ahGzku6zF6C7uhsq4/s320/PICT0019.jpg" width="320" /></a></div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg0mCEkDoR3veXtccLg_eVm7ROyO4zw7iuFaijX5jWhdRc_uru9pT1oQNYeTNDJLqNrtAJTFGtRL15nViFCimGeu9jZuSKyninxALmZ_VIvuFHCV8MKlheQ4R7wvc-LlOuMVZTVvYIkOG4FyPaLqBp-JoLRu4B09EuQrDXegUwg-qG2ckaXmj7UF40qCuQ/s1920/PICT0022.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1440" data-original-width="1920" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg0mCEkDoR3veXtccLg_eVm7ROyO4zw7iuFaijX5jWhdRc_uru9pT1oQNYeTNDJLqNrtAJTFGtRL15nViFCimGeu9jZuSKyninxALmZ_VIvuFHCV8MKlheQ4R7wvc-LlOuMVZTVvYIkOG4FyPaLqBp-JoLRu4B09EuQrDXegUwg-qG2ckaXmj7UF40qCuQ/s320/PICT0022.jpg" width="320" /></a></div><br /><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;"><br /></div><div style="clear: both; text-align: left;">Update: after some helpful comments on the <a href="https://www.reddit.com/r/cowboybikes/comments/18f5jl9/cowboy_battery_disassembly_and_rear_light_fix/" target="_blank">reddit thread</a> and chatting to user <i>icannotfindagoodname</i>, I desoldered some components and tested them in isolation. The diodes and resistors all seemed fine but the fuse (F1) showed weird behavior. As I was seeing a short if I put a voltage across all of the LEDs I traced the circuit to see where those points connect. This turned out to be the capacitor. I shorted the fuse and put in a beefier 10 uF electrolytic capacitor I had lying around, I applied 32V and a PWM signal and voila, there was light!</div><div style="clear: both; text-align: left;"><br /></div><div style="clear: both; text-align: left;">I ordered new capacitors, a resettable fuse and a new 100uH inductor (the specs of the ZXLD1362 LED driver suggested a bigger one than the 68uH that was used. The battery voltage can apparently reach 42V.)</div><div style="clear: both; text-align: left;"><br /></div><div style="clear: both; text-align: left;">Here is what I ordered at Farnell:</div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEghA1IAlPnX630CgMTPfLYmqNRnX1QgZxudmiMWe-bJOqjBjutFuCI265hRLBSWBMo6X6bWTerjf2g0SdACFXvYNlPOD34T3DXNmAghJABoWV2dC8TQpPJPBQVLiJVqjoj7nYFtcovC1r3wJDVDtjTEbn-JuCx-kkbf6EsO8AOI4C2_BlXuaOwq7GAIGCc/s443/Screenshot%20from%202024-01-04%2008-38-35.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="317" data-original-width="443" height="229" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEghA1IAlPnX630CgMTPfLYmqNRnX1QgZxudmiMWe-bJOqjBjutFuCI265hRLBSWBMo6X6bWTerjf2g0SdACFXvYNlPOD34T3DXNmAghJABoWV2dC8TQpPJPBQVLiJVqjoj7nYFtcovC1r3wJDVDtjTEbn-JuCx-kkbf6EsO8AOI4C2_BlXuaOwq7GAIGCc/s320/Screenshot%20from%202024-01-04%2008-38-35.png" width="320" /></a></div><div><br /></div>It arrived within 2 days, pretty cool. Now I put everything in place and re-assembled the battery.<br /><div style="clear: both; text-align: left;"><br /></div><div style="clear: both; text-align: left;">I put in the LED module last, so that I could reach the connectors through the hole. Then I glued it shut using hot glue. Then I noticed some small cracks in the black plastic enclosure for the LED module. This probably happened because of the force I applied when removing it. As another reddit user pointed out that his battery died in the end because some water got in that way, I put super glue on the cracks to hopefully prevent the same fate. On close inspection the glue is visible and it doesn't look as clean as before, but better than the entire battery becoming e-waste.</div><div style="clear: both; text-align: left;"><br /></div><div style="clear: both; text-align: left;">After the whole ordeal the light seems a bit more dim than before, though I might just be imagining that. It could also be because of using a slightly different resistor at R4 because I damaged it while desoldering and testing. I really need to buy some SMD soldering equipment.</div><div class="RY3tic" data-latest-bg="https://photos.fife.usercontent.google.com/pw/ADCreHfl1IhkiDaGmx-7rcQGqrK1p-Q04ygiPOg2vph2orQXn1giWe2HnuABWw=w152-h203-no?authuser=0" style="background-image: url("https://photos.fife.usercontent.google.com/pw/ADCreHfl1IhkiDaGmx-7rcQGqrK1p-Q04ygiPOg2vph2orQXn1giWe2HnuABWw=w152-h203-no?authuser=0"); opacity: 1;"><div aria-hidden="true" class="eGiHwc"></div><div aria-hidden="true" class="KYCEmd"></div></div>Jouke Walesonhttp://www.blogger.com/profile/12215646682936082041noreply@blogger.com1tag:blogger.com,1999:blog-4710497652927856789.post-11127944371261154202023-08-18T21:43:00.009+02:002023-08-27T12:37:06.593+02:00Capitalism's Greatest Trick<p></p><div>[ this is a draft ]</div><div><br /></div><div>Capitalism's greatest trick has been to give companies legal rights, thereby channeling very strong human instincts towards economic prosperity.</div><div><br /></div><div><b>Part 1 - Survival instinct is extremely powerful</b></div><div><br /></div><div>There are human forces that drive our behavior. These have been shaped by basic evolutionary measures. Here are two, but there are more.</div><ul style="text-align: left;"><li>All humans have an evolutionary developed need for survival. <b>Survival instinct </b>is real and a very strong force. People under existential threats will do amazing things to survive.</li><li>Some humans have a <b>competitive desire </b>to conquer, to wage war and take resources of others. The competitive desires results in higher chances of passing on genes, but is less strong than Survival Instinct.</li></ul><div>In most of human history, these forces have focussed on basic human needs for survival and gene reproduction: owning food, land, power, sexual relations.</div><div><br /></div><div><b>Part 2 - Humans act as groups </b></div><div><br /></div><div>As humanity progressed, humans have collaborated and acted as groups. Humans strongly identify with the groups they belong to, sometimes exchanging their own values and needs for those of the group. People have fought wars for their countries, religions, etc.</div><div><br /></div><div><b>Part 3 - Companies are legally people</b></div><div><br /></div><div>In modern western law, there is the concept of a Legal Person. A Legal Person is something that has the rights that humans have. This includes companies. Companies have rights and obligations and employees are in a sense merely agents acting on behalf of the company.</div><div><br /></div><div>Unlike most giant organizations (countries, religions), most companies are short-lived. It is trivial and socially acceptable to start a company, to join or leave one.</div><div><br /></div><div><b>Capitalism gave companies super powers</b></div><div><br /></div><div>Extending basic rights to companies is the greatest trick that capitalism has ever pulled off. It gave companies legal rights independently of the rights of the humans that are working for it. Now companies can be born, can grow or can die just like humans. Thanks to humans acting as part of their group, this mechanism allows humans to channel their evolutionary instincts without hurting other human beings.</div><div><br /></div><div>This has two consequences:</div><div><ul style="text-align: left;"><li>Companies that have a competitive drive can result in the death of other companies, but not of human lives.</li><li>Companies that come under existential threats will find extremely creative ways to survive.</li></ul></div><div>I believe that giving these very strong human instincts to companies while separating the consequences from human suffering has been the key differentiator for our capitalist economy.</div><div><br /></div><div>Think about this the next time your company talks about crushing the competition. is under serious threat, I bet you can feel the survival instinct kicking in.</div><div><br /></div><div><br /></div><div><br /></div><div><div>Disclaimer: I am not a biologist or economist.</div></div><p></p>Jouke Walesonhttp://www.blogger.com/profile/12215646682936082041noreply@blogger.com0tag:blogger.com,1999:blog-4710497652927856789.post-34234938679957134142023-06-25T21:33:00.011+02:002023-06-26T15:23:07.377+02:00Parenting and the Big Mental Energy Dip<p>During my late teens and early twenties I had an incredible amount of energy. And I would mostly put that to good use. When I was a parent it collapsed incredibly hard, but the good news is, it does come back! This slightly self-absorbed post (sorry about that) is written to celebrate that but also as an encouragement to other parents.</p><p>I've always been blessed with a lot of energy to get things done.</p><p>1 - When I was 16-18, I worked 20+ hours per week while being a full time high school student. I also set out to watch all IMDB Top 250 movies within a year or two. On many weeknights I watched 2, 3 or 4 films after 9pm. I would go to bed around 4am and be at school at 8:20am. Healthy no, but it was fun and I did it for a long time. I never finished all 250, but got really close!</p><p>2 - At ages 18 to 22 I went for two full-time university degrees in Computer Science and Artificial Intelligence simultaneously. I also started my own company, joined two study groups and had plenty of parties.</p><p>3 - Between 22 and 25 I started working, I learnt new computing paradigms, started a tech blog for the awesome company I had joined, I kept studying part-time and did an incredible amount of side projects. I also switched from Cloud Engineer to Product Manager and again partied a bit more than was good for me.</p><p><b>But then at 25 I got my son</b>, a true bundle of joy. It was hard work but I still managed to have a decent work/life balance (in favor of work). When my wife got pregnant again and delivered twins at 27, things changed. My work dropped down to mostly 9 to 5. I was always tired. Side projects got replaced by watching Netflix and falling asleep next to the kids when putting them to bed, if I closed my eyes for a second too long.</p><p>I saw this meme and something didn't feel right:</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjYItPg8-Z59BF03jASo4LlnE18MR3qMkkTUKMq1dDlNY1mnvWqdLWjQgu-GcsWb6nQpG-UJ1V3-2q20oOh3ycY6258Iy7dwEAhSPdUZFabY0BFENFm8uhrNJA2JzwAU3HSBATTRZVf-YNHoUSVutnc26LnRSM6LKpibZimaewzIE0AngZMwmhruYUWcEg" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="443" data-original-width="640" height="222" src="https://blogger.googleusercontent.com/img/a/AVvXsEjYItPg8-Z59BF03jASo4LlnE18MR3qMkkTUKMq1dDlNY1mnvWqdLWjQgu-GcsWb6nQpG-UJ1V3-2q20oOh3ycY6258Iy7dwEAhSPdUZFabY0BFENFm8uhrNJA2JzwAU3HSBATTRZVf-YNHoUSVutnc26LnRSM6LKpibZimaewzIE0AngZMwmhruYUWcEg" width="320" /></a></div><br /><p></p><div>I should be an adult (check) with no time (check) but with money (no, because I joined a startup, had 3 kids and was a single income parent) and energy (erm, no, I was exhausted). At this phase of my life it felt like I had nothing of the three. It wasn't awful by any means, but very tiring.</div><div><br /></div><div>But then around 2020/2021 I noticed I began to pick up more side projects again. I started exercising for the first time in my life, I started meeting up with friends much more. When I started freelancing the money also got a lot better. The kids are now 9, 7 and 7. A year or so ago I was taking care of them on a Saturday but fell asleep on the couch. When I woke up the house was eerily quiet. I felt like something was wrong and I should be worried but also like nothing was wrong. That's when I realized my kids are old enough to take care of themselves, and they are now OK to run around the neighborhood. It really was the end of an era. Still no time, but money and energy: yes. I also have three amazing kids to spend the energy and money with ;)</div><div><br /></div><div>I have to say it feels good to be back. If you are a struggling parent: it will get better.</div>Jouke Walesonhttp://www.blogger.com/profile/12215646682936082041noreply@blogger.com0tag:blogger.com,1999:blog-4710497652927856789.post-12845278443971061402022-11-27T15:01:00.009+01:002022-11-28T20:34:55.370+01:00Why Only Type-hints?<p>Whether you're working with Javascript / TypeScript or in Python, type annotations are a thing. On Hacker News we've seen big debates on whether types improve code quality or are just cargo-cult slowing us down. Now here's the thing: I've come to believe there's something very backwards in our approach to type hinting. Let me explain why.</p><p>Historically most languages were designed with types baked in. Think C, Fortran and probably most languages one level above Assembly. Types are a simple idea and were needed so that the compiler could figure out what operations to perform, e.g. the <b>+</b> operator has defined behavior on integers, and another on strings. With dynamic languages such as Python or Javascript, types were handled in the interpreter at runtime and no longer necessary in the language itself. In the last decade, with the rise of TypeScript and Python type hinting we're adding types back, not because the language needs them, but so that tooling can help us with the <b>correctness</b> of our software. I believe adding type hints helps with that but why are <i>types</i> the only language affordances to add when we are trying to improve correctness? We should be asking ourselves a different question: Are most of our software problems actually with types? I believe we are short-sighted in that we simply try to add features we've already seen in other languages. We are thinking inside the box.</p><p>If I look at most problems in the codebases I've managed, I've seen errors in performance, side-effects, security, etc. To be honest, types or not the first thing on my list. Now why do type hints improve quality? They allow the compiler to reason about the software that we make and it helps us to check our assumptions, faster than any human could do. We should allow our programming environment to help us with other aspects too. Here's an over-the-top example for Python:</p><p><script src="https://gist.github.com/jtwaleson/aa981b3095bb47c1e8ac698117cc572a.js"></script></p><div><b><br /></b></div><div>When we put our expectations down, the system can help us with the following kinds of problems:</div><ul><li><b>Performance</b>: a method was slow on the production set, but another developer didn't know and used it in the UI anyway.</li><li><b>Side-effects</b>: developers are not sure if functions are pure or if it could lead to external APIs being called, so how do we handle that if there are database rollbacks?</li><li><b>Security</b>: it's not clear who is allowed to execute this function, is it only for system administrators, customer service employees, or end-users?</li><li><b>Data Privacy</b>: Is there any PII going into the data lake that is only supposed to contain anonymized data?</li><li><b>Memory</b>: This function handles incoming video files and buffers them in memory. It will break when someone uploads a 1GB video file to your tiny server.</li><li><b>Intent</b>: the intent of the original developer is completely unclear. We can use free-form comments and things like pydoc for this, but it's a horrible solution in my view.</li><li><b>Tests</b>: test systems are add-on tools to our languages, there's nothing in the language itself that allows us to ensure input/output or other kinds of tests.</li></ul><p>Now obviously this is ugly. There's no inference or other clever things, and it builds on the annotation functionality in python which makes it only available at runtime. We want it when we are writing our software. I'm not proposing this syntax, but I do want a programming environment where we can add other hints than just type hints. It will probably never fit on one line like current type annotations do. But I do believe we will find solutions for this in the future.</p><p></p>Jouke Walesonhttp://www.blogger.com/profile/12215646682936082041noreply@blogger.com0tag:blogger.com,1999:blog-4710497652927856789.post-25775468891384379122022-11-06T13:07:00.010+01:002022-11-06T17:58:49.577+01:00Things I Didn't Build Yet<p>Here are some ideas I've been thinking about for a long time. In some cases I've started a prototype, in others it's just some thoughts. There are too many ideas for me to build in a lifetime, so I'm just posting them here, even though I might work on some in the future. Ideas are cheap, execution is hard. If you're interested in starting a business based on this, please let me know as a courtesy :) I just want these solutions to exist so I can use them.</p><p><span style="color: #0000ee; text-decoration-line: underline;"><a href="https://news.ycombinator.com/item?id=33491801" target="_blank">Discuss these ideas on hacker news</a></span></p><h2 style="text-align: left;">CloudSound</h2><div>Software businesses are silent. Let's change that. CloudSound makes your business events audible from a Chrome tab. You stream your logs to cloudsound and you can hear what's happening with a custom soundboard. A payment made? Ka-ching! An error? <alarm>. New customer? Get in tha choppah! At some point if there's a lot of traffic it can be made into acoustically pleasing ambient sounds. The way game music changes if there's an end-boss. The idea is that we are already overstimulated visually with dashboards etc, but there is some room for not-too-annoying ambient audio.</div><div><h2>GitShuffle</h2><div>GeoGuessr for code. You are dropped in spotlight mode into a codebase, and you have to guess which repo, which file and which line. Enterprise edition: do this on-prem with your own codebases, so you can learn them in a playful way.</div><h2>RichMeet</h2><div>Online meetings are stuck in 1950. We should have:</div><div><ul><li>agenda with timekeeping built-in</li><li>explicit decision points with voting mechanisms, notes are automatically taken</li><li>see who's in the meeting, what their background is and what their role is during the meeting</li><li>the ability to add someone by phone into the meeting (e.g. the meeting dials you, not the other way round)</li><li>automatic feedback on your audio and video quality, you are often annoying the hell out of other people without realizing</li><li>collect feedback about the quality of the meeting (content wise) from everyone, so the organizer can improve</li><li>bit creepy: engagement analysis based on contribution, gaze and facial expressions</li></ul><h2>DocuSnooze</h2></div><div>Documents accumulate on drives within companies. Storage is too cheap but attention is not. What if we wipe all documents that have not been accessed or modified in the last year(?). Each document has a clear owner, who gets an email "these documents will be deleted next week". You can snooze docs from within the email, or permanently prevent folders from being trimmed.</div></div><h2 style="text-align: left;">Stacky Bird</h2><div>A program that teaches kids simple programming by solving puzzles and having a bird manipulate a stack. Made a prototype at <a href="https://game.stackybird.com">https://game.stackybird.com</a></div><h2 style="text-align: left;">TechMap</h2><div>Have all software companies in a country and puts their office locations on a map. Filter on product vs. services companies, funding type, org size. Find the company you want to be with, that's close to you, not based on who can post their job openings the best, but on what companies are out there. Sometimes a company is perfect for you but not hiring right now, would be nice if you knew about it! Needs to be crowd-sourced with all the difficulties of that... However, if it works, it makes the software market much more transparent. Power to the people!</div><h2>Social Gravity</h2><h2><p style="font-size: medium; font-weight: 400;">A tool to explore social networks in a 2d map, people cluster towards who they know. You can learn so much about people's and group's interests, it's creepy.</p></h2><h2 style="text-align: left;">KnowledgeGraph</h2><div>A personal information manager with concepts, people, organizations. The data can be explored via a map / tech-tree interface. Your memory might suck, but we can augment that. E.g. you can instantly pull up the names of your friends' kids, get reminders on their birthdays, etc. Also useful for documenting "mandatory" knowledge within a team. Can be gameified a bit so you can level-up your knowledge within an area. Gives you clear input for your "personal development plan".</div><h2 style="text-align: left;">CompetitorWatch</h2><div>Automatically keep track of your competitors using web-scraping and other insights. Super useful for Product Managers and CEOs.</div><h2 style="text-align: left;">NextGenCode</h2><div>Software systems are full of vestigial parts. We are still just slowly abstracting how chips actually work, while we should be focusing on what developers need to get done. A modern web-stack needs: server language, databases/ORM, client-side app, styling. Then there's monitoring, alerting, logging, backup, the IDE, debugging, requirements, documentation, compliance, testing, It's a lot. Most of these components are bolted on to programming languages with duct-tape. We need something simpler and better, built from the ground up. Software is unique in that it's so flexible, I know there's a better solution out there.</div><h2 style="text-align: left;">ClassicMovies</h2><div style="text-align: left;">There are too many new movies in cinemas. There's about 100 years of film history, but you can only view 2 or 3 years in a theater. It would be great if there were more, let's build a platform that shows you what's out there and stimulates new ways of showing old films.</div><h2 style="text-align: left;">PersonalDashboard</h2><div>Keep track of whatever you want. Your habits, exercise, weight, work, learning. Select pre-sets (I want to be the perfect suburban father with a dad-bod / I want to be an entrepreneur / I want to become a tenured professor) and the platform gives you a roadmap and KPIs on the behaviors you need to stick to to get there.</div><h2 style="text-align: left;">CloudLab</h2><div>Mass-spectrometer lab software often runs on workstations in the lab itself. It would be great if there was a manufacturer-independent software solution to perform the analysis in the cloud. It decouples the location of the labs with the technicians of the analysts with the software.</div><h2 style="text-align: left;">VisualRegression</h2><div>Include a visual output of your software in your CI run, with differences with the previous versions highlighted. On all supported device screens etc. It would be even better if there was a real-time feedback option for this. Think webpack-debug server but you see the rendered app on 20 screens at the same time. An AI monitors for abnormalities (e.g. text overflowing buttons) and warns you instantly.</div>Jouke Walesonhttp://www.blogger.com/profile/12215646682936082041noreply@blogger.com0tag:blogger.com,1999:blog-4710497652927856789.post-82693524332504177662022-11-01T23:23:00.024+01:002024-03-03T16:38:54.744+01:00Gear<p>It's 2022 and I am a wannabe minimalist that owns way too much stuff. A small part of my belongings are the things that I really value and/or use every day. This post is about that kind of <b>gear</b>. If anything on this list gets lost or breaks down, I will instantly buy it again.</p><p>My three thoughts about "stuff".</p><p></p><ul style="text-align: left;"><li>Material goods do not matter, so try not to think or worry about them any more than absolutely necessary. (Am I doing that by writing this post? Maybe...) A corollary: I never get insurance for anything that I can pay out of pocket without breaking a sweat.</li><li>We are destroying the planet, let's try to limit our footprint. Reduce, reuse, recycle, in that order.</li><li>Good tools are delightful and indispensable.</li></ul><h2 style="text-align: left;">Clothing.<br /></h2><div>I like to wear the same clothes on most days so I don't have to think about what I wear. I own about 2 to 6 of each item below and cycle them throughout the week.</div><p></p><ul style="text-align: left;"><li>Jeans, no brand. € 40. (update: Switched to classic Levi's 501 after discovering that stretchy jeans don't last long as pure cotton)</li><li>Brown leather belt. € 20.</li><li>New Balance shoes, typically type 997. I exclusively buy New Balance simply because it limits my search space for shoes and I have to think less. Why NB? I don't really know, but lucky them, a customer for life. € 100.</li><li>Black t-shirt, no brand. € 10.</li><li>Black hoodie, no brand. I only wear this sometimes when it's cold. € 40.</li><li>Socks, my startup company branded. Free.</li><li>Underwear, bought at supermarket. € 10.</li></ul><div>Total value: € 180 . (220 in winter because of the hoodie)</div><h2 style="text-align: left;">Carrying around.</h2><div><ul style="text-align: left;"><li><a href=" https://www.knivesandtools.com/en/pt/-swiss-tech-shswtu-utili-key.htm">Mini knife / multi-tool.</a> Love this thing and bought a lot at AliExpress. € 5.</li><li>The North Face Vault backpack. Got it for free at work 8 years ago.</li><li>Keys, handkerchief, drivers license and debit card. "Free".</li><li><a href="https://www.charlietemple.com/nl-be/p/brillen-the-sheer-jet-black/10283">Glasses from Charlie Temple</a>. Fleur from work recommended this style, and it's expressive, I like it. € 60. (Pro-tip: always buy coated lenses, being too cheap-ass here hurts).</li><li>Simple gold wedding band. Inherited from my great grandmother, so free, but high sentimental value.</li></ul><div>Total value: € 65.</div><h2 style="text-align: left;">Portable electronics.</h2><ul style="text-align: left;"><li>iPhone SE 1st gen from 2016. Love this phone, was on budget Android phones until this year but they are getting way too big. I picked up my wife's old iPhone for free. The only problem is the battery life. It was free, but I paid € 40 for a replacement battery (which still sucks, or maybe I'm just on my phone too much).</li><li>Tempered glass protector for the phone. € 5.</li><li>Xiaomi Mi earbuds. I listen to way too many podcasts on this. € 25.</li><li>My homemade e-bike remote control key. € 30.</li><li>Kindle Paperwhite. € 100.</li><li>Casio F-91w wristwatch. The classic and emblematic of my "stuff" philosophy. € 15.</li><li>Anker 10000 mAh PowerCore powerbank. Because of the crappy iPhone battery. € 40.</li><li>I also carry one or two books, a writing pad, and a whole kindle library, but will not count this.</li></ul><div>Total value: € 255.</div></div><h2 style="text-align: left;">Home-office setup.</h2><div><ul><li>ThinkPad T495s. Every +/- 6 years I buy a second hand T series in mint condition two years behind the latest gen, that's most cost-effective. Should have gone with Intel this round, battery sucks on Linux. € 600 euro. ( update: upgraded to a T14s Gen2 )</li><li>Das Keyboard 4 Ultimate. Clicky keys without print. Love it. € 150.</li><li>Logitech Mx Master 3. Best mouse I've ever used. Super comfortable. € 100.</li><li>RODE NT USB Microphone. Someone on HN said you sounded smarter with a good microphone so I bought it. € 150.</li><li>Kyocera M5526cdw all-in-one laser printer. This type is apparently extremely cost-effective when printing a lot, which I don't do. It's always useful and my kids like to use it for school stuff. € 600. </li><li>Philips 34" wide-screen display with USB-PD. € 550.</li><li>Logitech B525 HD Webcam. It was the only available webcam when covid lockdown started. Now I'm working in the office a lot more where I have a C930(?), so the B525 will do. € 100.</li><li>A lamp that shines in my face so it looks less dark. € 20.</li><li>Ikea standing desk. € 180.</li><li>Always-on Intel NUC from 2014 with 6TB BTRFS storage on USB disks. € 800.</li><li>Desktop computer with Ryzen 3600, 24GB, GeForce GTX 1060 6GB, 2x 1TB NVME storage. € 1000.</li></ul><div>Total value: € 4250.</div></div><div><h2 style="text-align: left;">Transportation.</h2></div><div><ul style="text-align: left;"><li>A Cowboy C3 e-bike. It goes _fast_ and is very sporty. Ride it every day, sometimes 2x 45km to work in Amsterdam. Amazing! € 2500.</li><li>We recently sent it to a garage to sell, but still worth an honorary mention. A 2008 Dacia Logan MCV 7-seater, Ambiance (cheapest) edition. Dacia originally brought it onto the market for an incredible 10k euro. It had no electric windows, the AC was broken for years, it smelled funny and something made a funny / scary sound near the engine. My daughter Suzanne found a rock and scratched a drawing on the side when she was 3 and I wast very proud of her and the picture, I always showed it to people. The car had two different size side mirrors because I bought the wrong type when one broke. The Dacia fits so many people and so many things! It's incredibly practical. I see cars as a necessary evil, and necessary evils should be ugly, spartan & cheap. At the same time I loved this car deeply because of its character and all the great things we did with it. We are now trying to live without a car for a couple of years. € 3500.</li></ul><div>Total value: € <strike>6000 </strike>2500.</div><div><br /></div><div>Mandatory picture of my ex-car. </div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgdjLbcdR_0mjg7Vfv1zwMzGixWPQzB-yF-xV7pd0OC_dmYPMuA9KMm9oEG-Lg-kpkUFvYHAYfPEdDchjBnz1e7rqIjQmkcwozW7dWvOqiu3PgMqaG0WwwOxPCKHF89e6ayW0eB5tHnXy2KwLKkwx9wjw2lIviKOUQ4VbxW_2npkO71wcpaTH3NzbEh/s2048/5587546d-f48b-4ce8-acbb-4757e435512e.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1536" data-original-width="2048" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgdjLbcdR_0mjg7Vfv1zwMzGixWPQzB-yF-xV7pd0OC_dmYPMuA9KMm9oEG-Lg-kpkUFvYHAYfPEdDchjBnz1e7rqIjQmkcwozW7dWvOqiu3PgMqaG0WwwOxPCKHF89e6ayW0eB5tHnXy2KwLKkwx9wjw2lIviKOUQ4VbxW_2npkO71wcpaTH3NzbEh/s320/5587546d-f48b-4ce8-acbb-4757e435512e.jpg" width="320" /></a></div><br /><div class="separator" style="clear: both; text-align: center;"><br /></div><h2 style="text-align: left;">Software.</h2></div><div>My software stack is super stable, and basically unchanged since 2015. Am I stagnating and missing out or did I reach peak performance? I use these programs almost every day:</div><div><ul style="text-align: left;"><li>Debian testing.</li><li>i3.</li><li>Chrome.</li><li>VIM (Neovim but I couldn't care less).</li><li>zsh / oh-my-zsh.</li><li>git (I'm a bit in love with the git data structure so I have to mention it)</li><li>tig</li><li>nmon</li><li>Spotify.</li><li>Syncthing (started using it in 2022).</li><li>Silversurfer / ag.</li></ul><div><br /></div></div><div>(added in March 2024):</div><h2 style="text-align: left;">Electronics:</h2><div>I set up a bit of an electronics lab recently and wanted to recommend the following products:</div><div><ul style="text-align: left;"><li>Soldering station: Aixun T3a - T245 handle. After upgrading from an old entry level soldering station I did research and landed here. Amazing! It heats up in 3 seconds. € 150. </li><li>Oscilloscope: Siglent SDS1104X-E. I wanted an Oscilloscope for the last 20 years but never needed one. Bought this and it changed my understanding and skills in electronics a lot. Super happy with it. € 550.</li><li>Power Supply: Korad KD3305D. Great stuff. € 200.</li><li>Function generator: UNI-T UTG962E. Surprisingly useful and fun! € 150.</li></ul></div><div><br /></div><div></div><p></p>Jouke Walesonhttp://www.blogger.com/profile/12215646682936082041noreply@blogger.com1tag:blogger.com,1999:blog-4710497652927856789.post-21823577988983422852022-10-02T16:16:00.180+02:002024-03-03T19:05:14.694+01:00The software that I love<p>[ this is a long article, almost the size of a short book ]</p><p>Pride. When interviewing candidates I always ask "tell me something that you are proud of." Sometimes you get a blank stare. That person is down and dealing with some shit. Luckily most of the time the eyes light up and the person starts talking about <i>them</i> <u>at their best</u>. I love those moments. As in Peter Thiel's "From 0 to 1", I want to see the best in people and then see if we can deal with the bad. In the end, I ask "so this is all great, but what will we have to learn to live with if we hire you?". On the one hand I'd like to prepare myself, but the question tests for self-reflection too. It works well.</p><p>But back to proud. I've been dealing with some shit too, and there have been a bit more downs than ups lately. This blog post/diary is a way to make my eyes sparkle and remind myself of the things I have built and still love. These are short stories in chronological order. There's a lot more to life than software, but this focused format is simple and it works. I might write from other perspectives in other posts. I've written this post for myself, but if you enjoy it, that's all the better.</p><p>These stories are how I remember them. My memory isn't amazing and in most of them I only had a small part with a limited perspective. If you have additions, questions or other feedback, send me a message at jouke@waleson.com.</p><p>Note 1: This is a work in progress and will be updated when I have time.</p><p>Note 2: Wow, this post got long but also popular. It had over 10k views on day 1. To make it easier here are some links:</p><p></p><ul style="text-align: left;"><li><a href="#part1">Part 1 - Fun but somewhat forced programming ('96-'00)</a></li><ul><li>[added Oct 2.] 1996 - JTJ Rekenen</li><li>[added Oct 2.] 1997 - Plotter</li><li>[added Oct 2.] 1999 - Reverse engineering the binary format of HP ChemStation</li></ul><li><a href="#part2">Part 2 - High School & Uni ('00-'11)</a></li><ul><li>[added Oct 2.] 2004 - The HP 49g</li><li>[<span style="background-color: #d9ead3;">added Oct 3.</span>] 2005 - Porting an Atari tax program to Delphi</li><li>[added Oct 2.] 2009 - SnelTrein</li><li>[<span style="background-color: #d5a6bd;">added Oct 30.</span>] 2010 - Snapp</li></ul><li><a href="#part3">Part 3 - The Mendix Years ('11-'18)</a></li><ul><li>[<span style="background-color: #93c47d;">added Oct 8.</span>] 2011 - newnode.py</li><li>[added Oct 2.] 2011 - Social Gravity</li><li>[added Oct 2.] 2012 - autopullpep8 and the facebook adventure</li><li>[added Oct 2.] 2012 - JUDO</li><li>[<span style="background-color: #d5a6bd;">added Oct 30.</span>] 2012 - Decrypt.py</li><li>[added Oct 2.] 2012 - Mendix WebModeler v0.1</li><li>[<span style="background-color: #d9d2e9;">added Oct 5.</span>] 2012 - mxplient</li><li>[<span style="background-color: #f4cccc;">added Oct 6.</span>] 2012 - Access2Mendix</li><li>[<span style="background-color: #ffd966;">added Oct 9.</span>] 2013 - Stacky Bird</li><li>[<span style="background-color: #d9ead3;">added Oct 3.</span>] 2014 - Sandboxes</li><li>[added Oct 2.] 2014 - cf-mendix-buildpack</li><li>[<span style="background-color: #d9d2e9;">added Oct 5.</span>] 2015 - certinator</li><li>[<span style="background-color: #3d85c6;"><span>added Nov 28</span><span>.</span></span>] 2015 - buildpacks for Docker</li><li>[<span style="background-color: #d9ead3;">added Oct 3.</span>] 2015 - InstaDeploy</li><li>[<span style="background-color: #d9d2e9;">added Oct 5.</span>] 2015 - Mendix2Java</li><li>[added Jul 25] 2016 - Mendix Cloud v4 - Part 1 - How it got started</li><li>[<span style="background-color: #ffd966;">added Oct 9.</span>] 2016 - Packing all mxruntimes in git</li><li>[<span style="background-color: #d9d2e9;">added Oct 5.</span>] 2016 - The too clever scheduling service</li><li>[added Oct 2.] 2017 - The Reaper</li></ul><li><a href="#part4">Part 4 - CTO @ easee ('18-'23)</a></li><ul><li>[added Oct 2.] 2018 - The easee Wizard Engine</li><li>[<span style="background-color: #e69138;">added Oct 10.</span>] 2019 - The easee Diff Viewer</li><li>[<span style="background-color: #ea9999;">added Oct 11.</span><span style="background-color: white;">]</span> 2019<b> - </b>easee RemoteControl</li><li>[<span style="background-color: #cfe2f3;">added Oct 4.</span>] 2020 - easee TeleVisus</li><li>[<span style="background-color: #274e13;"><span style="color: white;">added Oct 22.</span></span>] 2020 - QMS Integrity Checker</li><li>[<span style="background-color: #fce5cd;">added March 3 2024</span>] 2021 - The WaterRower Camera</li><li>[<span style="background-color: #d5a6bd;">added Oct 30.</span>] 2021 - FDA Crawler</li><li>[<span style="background-color: #ea9999;">added Oct 11.</span><span style="background-color: white;">]</span> 2022<b> - </b>Cowboy e-Bike Remote Control</li></ul></ul><p></p><h1 id="part1" style="text-align: left;">Part 1 - Fun but somewhat forced programming ('96-'00)</h1><h2 style="text-align: left;">1996 - JTJ Rekenen</h2><p>Niels, my dad, had a 13-year career at HP (later Agilent). He started servicing mass spectrometers and ended up as a programmer. He always loved computers. He was pushing me to get into software from about age 8 or 9. We started developing a small program together that taught kids how to do maths. My dad did most of the work, and we created this together with my friend Joscha. We called it JTJ Rekenen. JTJ stood for "Jouke-Thiemo Joscha", Rekenen is Arithmatic. We contemplated calling it JJT but JTJ sounded cooler. The program did simple addition, multiplication etc. In hindsight, I believe the logic for division was full of problems.</p><p>The UI let you practice +, -, * and /, and turned the screen green if the answer was good and red if bad. I sold this little program to my school for about 50 guilders. I was pretty proud, but in reality my dad made most of the software while my friend and I watched and did some simple things and nodded a lot. JTJ Rekenen was built in Delphi or Visual Basic. I forgot which, but we used both at this time.</p><h2 style="text-align: left;">1997 - Plotter</h2><p>As my dad worked at HP he saw all kinds of equipment in the office. At some point he brought home a couple of broken inkjet printers. He took out a lot of parts, ordered a microcontroller from the US and created a simple plotter for A4 paper with the stepper motors. Back then microcontrollers were expensive and difficult to program, there was no Arduino IDE or anything like it. If you weren't careful you could blow out your parallel port.</p><p>Dad built all the hardware, connected the microcontroller, and hooked up a driver and a small library so we could control it from Delphi. My job was to program the code to move the head around and up and down. Up means the marker is not touching the paper, down means you're drawing. This was great, as you could program something and see the results in the real world right away! I still enjoy try to get instant feedback loops whenever I'm working on a system.</p><p>I started with creating the logic for the letters. If you draw an A, you had to create 3 lines, 2 diagonal and 1 horizontal. On and on for the letters B to Z. It was quite a lot of work but after some time I got good at it and start optimizing patterns. Dad showed me how to make procedures to re-use logic. I figured out how to abstract things so that letters could be drawn from a relative starting position, how to do line wrapping, etc. Lots of fun.</p><h2 style="text-align: left;">1999 - Reverse engineering the binary format of HP ChemStation</h2><p>At this time of my life I did not program for fun, I did it because my dad asked me to. I did a lot of other things with computers though, mostly games and drawing (CorelDraw). But in the summer of '99, my dad was struggling with the software that Agilent used for their mass spectrometers, ChemStation. It was a pretty big software suite and development was done in California. He could not influence it. He was often frustrated with how it worked, and like a true hacker, wanted to read and manipulate the data files with his custom software. He still does that to this day, now mostly with ChemStation's successor MassHunter. The ChemStation data format was super optimized for storage space and thus hard to understand. It stored the raw data from the spectrometers and some data that was annotated by the analysts in the lab.</p><p>On Friday nights dad, me and my two younger brothers used to go swimming in Amstelveen next to the HP office. One night in the car on the way back, he said that he could use my help for a couple of weeks during the summer break and could pay me about 100 guilders for it. He wanted me to figure out the data format that ChemStation used, so that he could write a parser for it. He showed me how to manipulate the data in ChemStation, and then inspect the files with a hex editor. If I made a copy of the file and changed the data I could compare the before & after versions and see how things were stored. Wow.</p><p>I learned so much in these couple of weeks. I think this was the first time I actually created something useful for someone. He could have done it 10x quicker himself, but what a fantastic learning opportunity for me. I learned about little/big-endianness, hex/binary/string encoding, and variable length fields. The result was a pretty good description of the data format. When my dad implemented the library he would sometimes call me over to explain some details that he didn't understand. I felt awesome and was only 11 years old!</p><p>In 2010 I worked for him for a year, and one of my projects was to create a C# library for the same old ChemStation data format. I think he still uses it to this day.</p><h1 id="part2" style="text-align: left;">Part 2 - High School and Uni ('00-'11)</h1><h2 style="text-align: left;">2004 - The HP 49g</h2><p>In secondary school, everyone that did "advanced" maths had to get a TI 83+ calculator. Being an "HP family", my dad bought me an HP 49g+ calculator with RPN. I loved that thing. Going against the grain and doing things differently is very important to me. Here I had to get special permission from school to use this device because it was not the approved crappy TI calculator. Luckily I had a really cool maths teacher (hello Mr. Pieke!) and he made it happen.</p><p>I loved RPN and stack-based programming. I created all kinds of small programs in User RPL. On holidays I would be programming stuff in my tent. On hpcalc.org I downloaded all kinds of knowledge bases for e.g. the periodic table and loved reading things from the calculator. The calculator broke down at some point, then I bought a 49g+, but that was stolen from my student room later. I still miss it.</p><h2 style="text-align: left;">2005 - Porting an Atari tax program to Delphi</h2><div><div>My mom made me take side-jobs from age 13 and that's something that I'm really grateful for. It gave me a good work ethic and plenty of money for a kid, but I always spent it right away and was never able to save any. I started delivering newspapers, and then graduated to baking bread at the Jumbo supermarket. I got fired after a year because I overslept one too many times, and had to find something new.</div><div><br /></div><div>My friend Marijke from school had a neighbor, George, who was 18 and was starting a computer repair business. He was entrepreneurial and studied next to this full-time job. He also played in a band. He had so much energy. She recommended me to him because I was doing things with computers all the time. I was 15. The pay was terrible but I loved the work. I started with 4 hours per week, but by the time I was 18 was often doing 20-25 hours per week after school and in weekends. We started in his mom's house, and she would feed me Mexican food all the time. She was very sweet and I was way too skinny. Then we moved to a real store in the center of Middelburg and I had to arrange my own food.</div><div><br /></div><div>I could program, repair and assemble computers. We had two more employees, Lotte and Bart, who knew very little about computers and I had to teach them almost everything. The assembling was the most prestigious work. The repair was excruciating, especially when the hard drives were semi-broken and de-fragmenting would take ages. Or when there were nasty viruses. People came in with "very important data" that they could not afford to lose. That was mostly porn.</div><div><br /></div><div>There were so many mistakes (by me) that I don't know where to start. I lost computers under repair because we had no labeling system. Customers came in for weeks asking if their PC was done. I had no idea which one it was and had to play dumb. I was also very introverted and had to practice calling someone after a finished repair. Lotte and Bart were much better with those kind of things. Often I forgot the mainboard backplate when assembling the machine. Then I had to take everything apart and start over. I learned from my mistakes and became quite competent. I programmed our webshop, created a labeling system and built a couple of high-end PCs for rich customers.</div><div><br /></div><div>Then one day, George came in with a big stack of paper. George was a great networker and sales guy, and he had made a deal. The paper was the source code for an administration & tax program for a local store. The store owner was an old geek and had built his own software in Basic(?) in the 80s. The Atari it ran on died and he needed a replacement on Windows fast. George put me to work. I had to decipher the code but I did not know the language or how a terminal worked. I did not have a working version of the software or a computer to run it on, so had to reconstruct it in my head.</div><div><br /></div><div>I had to program the replacement in Delphi but I was also a complete beginner in that. It took me weeks to figure it out, and I'm not sure if George ever made money of the project. In 2012 it was still running, because the VAT rate in NL went from 19% to 21% and George contacted me to see if could update it. I did not have the source code anymore. Oops.</div><div><br /></div><div>I learned a lot from George and from all of the mistakes I made. Thanks for the fun and the opportunity BG!</div></div><h2 style="text-align: left;">2009 - SnelTrein</h2><p>While I was off studying Computer Science the smartphone revolution kicked off. Being an Apple hater, I got the first Android phone I could get my hands on: the T-mobile G1. Loved it and still think killing physical foldable keyboards was a shame. I had all kinds of ideas for apps, for some reason most of them revolved around trains. Like a GPS alarm clock that would wake you up when you got close to your destination.</p><p>I traveled by train a lot, and planning a trip or checking train times was a pain. I decided to build a train times app, but the name Trein was already taken, so I named mine SnelTrein. The Dutch name for high-speed trains. It was a simple wrapper around the mobile website of the NS (Dutch Railways). My app was super fast and super simple in the UI, as it remembered your trips. Your most often used trips were always on top. Once the app opened, it was literally one click to see the departure times of any of your 5 most used trips. I loved it, users loved it, and it grew to 3000 installs.</p><p>After some time, the NS released their own app, and I figured they are probably not scraping their own mobile site. After MITM'ing the traffic, it turned out they indeed had an XML based API which was password protected but they did not use HTTPS. I created a blog post post about this, submitted it to Dutch tech website tweakers.net. It was posted to the front page and I got my 15 minutes of Dutch tech fame. <a href="https://tweakers.net/nieuws/73527/ontwikkelaar-verkrijgt-toegang-tot-ns-api-voor-treintijden.html">https://tweakers.net/nieuws/73527/ontwikkelaar-verkrijgt-toegang-tot-ns-api-voor-treintijden.html</a></p><p>I stopped maintaining SnelTrein because the NS app caught up a bit and is good enough. Early in 2022 I also switched to an iPhone SE from 2016 because Android phones are so big.</p><h2>2010 Snapp</h2><div><p>Stephanie's uncle Rob had a small design / marketing agency in Amersfoort, together with Bart. They contacted me to create a web-based "company personality test". A bit like 16 personalities / MBTI but then for companies. I liked the idea and could use the money, so I went ahead and said yes. The basic concept was to get companies back in touch with their core to brand themselves more effectively. E.g. you might discover that your company is a true "pioneer" or "peace keeper". If you're in touch with your personality, it should give you a sort of compass for your actions and for how to communicate more effectively. It was about 12 questions and you had to drag and drop your 3 favorite answers per question. Then you got 1 of 12 different personalities at the end. After the test they could help you create a brand strategy and implement it.</p><p>About a year or two later Rob & Bart needed a similar website with a quiz, and they asked if I could create the same thing again. It was using crappy jQuery technology so I wasn't too excited about it. The Dont-Repeat-Yourself mantra kicked in, I had a bit of a feeling that there was a cool tech solution and decided to create an engine and editor for creating quizzes. This turned out to be a really cool engineering challenge. I went a bit wild on my own time and within months you could:</p><p></p><ul><li>create quizzes in diagrams with a drag & drop editor interface</li><li>embed it on any website</li><li>see live traffic as people were taking the quiz</li><li>have persistent data stored in mongodb</li><li>have non-linear presentations with paths dependent on previous answers</li><li>have a pub-quiz-like interface for many users</li></ul><p></p><p>Slowly but surely it was becoming a full-fledged programming language. I experimented with different names, and went through Snap, Snapp, SnapLogic, WebFlow, SurveyMaker. Most of these were taken by similar companies though. Rob & Bart were very impressed and really loved the direction this product was going in. We started talking about creating a company together. I would do tech, and they would find customers and do implementations. However, by the time that it became really serious it was mid 2013. I was very busy with my day job, getting a kid and interviewing at facebook. I couldn't afford to go all-in as we had no funding and I was pretty much living paycheck to paycheck. Besides, the tech at Mendix was a lot more exciting than this.</p><p>In conclusion Snapp was just too low on my list of priorities and it slowly died after this. In hindsight there was a lot of potential. I had a great time with my career choices and don't have any regrets, but still I sometimes wonder "what if" about this venture. We'll never know :)</p></div><h1 id="part3" style="text-align: left;">Part 3 - The Mendix Years ('11-'18)</h1><h2>2011 - newnode.py</h2><div><div>At Utrecht University I had done a dual BSc. degree in Computer Science and Cognitive Artificial Intelligence. Most if it was technical but I had done plenty of courses in the humanities as well. Some psychology, history of philosophy and Arabic. I was also lucky enough to have joined a book club where we read the classics. I made great friends there. One of them invited me to an honours program in Rhetoric in Amsterdam. That class had about 30 students, and Alexander Klöpping was one of them. I remember that he flew to NYC to get the first iPad and then talked about it on national television in "De Wereld Draait Door". He was in a very different world from all the other students and it was really cool to see. </div><div><br /></div><div>When I started working life I knew I was going to miss the humanities. I registered for an evening program for a BA in English Language & Culture at Leiden University, My first job was with my dad, and it didn't really work out. It was just me and him and I struggled with motivation or even to get out of bed. People in my English classes saw I wasn't happy. Being slightly alcoholic, I joined a gang that went out to a bar every Thursday after class. Annemarie, Maryssa and prof. Liebregts were there most of the time and we had lots of fun.</div><div><br /></div><div>One of the assignments for class was to write a job application in English. Annemarie wasn't happy in her job either and had met Derek, the CEO at this tech startup at a party of a friend. He was hiring sales people. She decided to write an actual application and got hired. Within a couple of weeks she told me that they were looking for developers too. It sounded cool and she introduced me to Johan, the CTO. After an email exchange I visited the old Mendix office on the Westzeedijk. I didn't realize it at the time, but actually Johan had only taken over the role from Roald a few months before. I think I was one of the first hires he did as CTO. Working at Mendix was an experience for which I will be forever grateful to everyone there, and a special mention to Annemarie for suggesting it to me. I ended up staying close to 7 years in various positions. </div><div><br /></div><div>Because I was running Ubuntu on my computer, Johan said, "oh Linux, just like the cloud guys. Let's put you in that team." I got hired, but actually I didn't know anything about cloud, servers, or even Linux. I just really didn't like Microsoft and Apple. </div><div><br /></div><div>We had two "cloud" teams. One was the SysOps team that was responsible for customer hosting & internal IT. The guys there were extremely good system engineers, and had built a rock-solid hosting operation in two colocation data centers in NL. The CEO didn't like it. By 2011 the cloud was the cool new thing and servers in a data center were obviously not the cloud. You know the old saying: If the CEO says Cloud, you just say "which one". They had looked at AWS and some others and had decided on Linode. It was more affordable and their culture fitted well with the hacker ethos of Hans, the senior systems engineer. </div><div><br /></div><div>As the 3-headed SysOps team was already drowning in work, a new team was formed. This was Arjen and Achiel, and later myself. Once I joined, Arjen went back to Runtime, his job in the cloud team was done. Achiel and I had to build the Cloud, while the SysOps team would be maintaining the serious production systems.</div><div><br /></div><div>I loved working with Achiel, he was so much fun. We had Mario stickers on the wall and were laughing all the time. We had a running joke that we could only use Dutch software to build the cloud: Python, Mendix and vim. He is a great engineer and now PM at CloudFlare, and he taught me so much. But if we're honest we both knew very little about systems and hosting. We were both just regular developers, he senior, I junior. In the cloud team we had to build productions systems and work with a lot of things out of our comfort zone:</div><div><ul style="text-align: left;"><li>Hosting Reverse Proxies, JVM based apps, Databases</li><li>Networking protocols: DNS, TCP/IP, HTTP, SSL/TLS, XMPP, Load Balancers, NTP</li><li>Servers: RAM, CPU, hard drives</li><li>Services: Backups, Monitoring, Alerting</li><li>Distributed Systems and operating them 24/7</li><li>Scripting in Bash, Python etc.</li></ul></div><div>It was a lot. I would say that on a 0-10 scale, the job required a skill level of 6 on all these, and I was a 1, maybe 2. Achiel was a bit ahead but we really needed the SysOps guys for a lot of guidance. By now I think I'm at 8 for most of them. Over time everything went wrong so often that I simply had to learn how it worked.</div><div><br /></div><div><div>Within a couple of weeks of me joining, Johan took a holiday and Roald started giving us feedback in a sprint review. Johan was always nice, but Roald gave us so much (justified) crap that I will remember that meeting for the rest of my life. It was a heated discussion and having recently joined I only observed. I still got a bit scared. It took me years to make a 180 and now I sometimes wish I was as clear and as passionate about user needs as Roald.</div></div><div><br /></div><div>My first task was to automate the creation of new cloud VMs. We had inherited a wiki page with instructions on how to install a virtual machine according to Hans' specifications. We would need at least 100s of VMs, and indeed, a couple of years later we had 6000+. Being a good lazy developer, I wanted to automate this. I created `newnode.py` which would spin up two new VMs on Linode (1 for app, 1 for database) and set up the infrastructure to run one Mendix app. We had a naming scheme for different data centers. Linode had Japanese dishes (appnode-unadon), Rackspace US: Star Wars (appnode-aldebaraan), Rackspace UK: Star Trek (appnode-stargazer).</div><div><br /></div><div>The script touched all the services & protocols above. The things that could not be properly implemented were using `expect`. I learned about "when using 'expect', expect trouble" the hard way. Despite my lack of knowledge with any of the Ops stuff, and being very new to Python, it was finished within a couple of weeks (or months?). I had this big smirk on my face, because I could do in a couple of minutes what the Ops guys were doing manually in an hour. Now to be fair, the script outputted a lot of information that we had to manually put in our DNS servers and other systems which only the SysOps guys had access to. It still took some years to automate everything end-to-end. Also, setting up a server is easy, maintaining it over time is hard. Nevertheless, it was a great step.</div><div><br /></div><div>Hans was very much against going to the Cloud in the way that we were doing it. A bit of Not-Invented-Here syndrome maybe, but also because of costs. He could run a more stable operation for about 5x less money because the hardware he set up was tailored to exactly our workloads. It helped that he probably forgot to include his own salary in the calculation, but it was still a lot cheaper ;) After a couple of years we ended up running the same kind of VMs on our own infrastructure in three different Data Centers in NL. It was the financially sensible thing to do. In the US we kept running on cloud VMs because we had no boots on the ground. In both setups we kept using newnode.py or an evolution of it. Hans' vision was always that if you go cloud, you should really go cloud-native instead of running the same old "One VM per app" approach. And until we would have that, we should stick with hardware. In that he was completely right. It would take years before we could get to that next generation infrastructure.</div><div><br /></div><div>The SysOps and Dev spirits kept clashing a bit over the years, until we decided to join forces and become the DevOps team together. Achiel and Johan had read "The Phoenix project" and were sold. We took the best of both worlds and it was great, and I think everyone on the team is still nostalgic for that special time in our lives. We won the Team Excellence award for it a couple years later.</div></div><h2 style="text-align: left;">2011 - Social Gravity</h2><p>I got onto twitter in 2010 and at some point realized the immense knowledge stored in the network. If you know who someone follows, and who follows that user, it tells you a lot.</p><p>With javascript webworkers I created Social Gravity. A 2d visualization of someone's network, all followers and their relationships. It had a small gravity engine, where you push away everyone by default, but if you follow someone, you attract them. If users get closer you repel them exponentially, so that the network does not collapse into a single point.</p><p>Magically little groups of your different spheres of influence or interests appeared. If you hovered over an area, it highlighted frequent keywords in the bios of the twitter accounts. You can try to market yourself in any way you like, but your network doesn't lie.</p><p>As twitter restricted their APIs a lot, and I was very busy with my cool new day job at Mendix, this project died down. I created a video with how it worked here: <a href="https://www.youtube.com/watch?v=pyVUMTVarpQ">https://www.youtube.com/watch?v=pyVUMTVarpQ</a> . It's a bit embarrassing because it tells you so much about my interests back then. Anyway, most social network APIs don't expose this data anymore, but they use this data for all kinds of advertisement targeting. Pretty scary.</p><h2 style="text-align: left;">2012 - autopullpep8 and the facebook adventure</h2><p>At Mendix, Achiel, my first team lead showed me Python and it became my go-to language. I got frustrated with the different coding styles, whitespace errors, and learned about pep8, flake, etc. Nowadays we use `black` but back then there was only `autopep8`. A tool that reformatted python code to the pep8 standard.</p><p>Learning about GitHub, its API and autopep8 I got a brilliant idea. What if I created a script to clone the top 100 most starred python projects, applied autopep8 and submitted a Pull Request. All completely automated. This was simple. I was nice enough to check the diffs before sending the PR. Autopep8 got a lot wrong and made some things really ugly. I spent 20 hours cleaning up code and then submitting PRs. I got my (small) contributions into a handful of top python projects. Pretty cool! Now more people figured out this trick and it is frowned upon in the open source community, but I think I did more good than harm.</p><p>I will never know for sure, but I suspect getting patches into top python libraries triggered some recruitment algorithm in silicon valley. In mid 2013 I got a message from a recruiter at Facebook for a position in their Production Engineering team. I actually hated Facebook and had deleted my account a few months prior. On the other hand the job was super cool and I loved California. I thought I had 0 chance of making the interview but it might be a great learning exercise. So, the process started. Over a period of 2 months I had 5 late night calls with the US West Coast that I had to keep secret from my bosses. I didn't prepare much and had the feeling that I passed every interview by the skin of my teeth. </p><p>My brain works a bit strangely. I can't grasp theoretical knowledge well, so while studying Computer Science a lot of concepts did not stick. But in these interviews, I finally understood what the engineers were struggling with and how we could create solutions. The theory finally had a useful application! I learned so much during and after these calls, and I'm super grateful for the experience. </p><p>At this time I was offered an "incentive package" at Mendix because of all the projects I did in my free time. I felt obliged to come clean and told Johan about my "affair" with Facebook, and that I appreciated the effort but first had some choices to make. I think the news spread quickly and most people considered me a lost cause. Facebook flew me out to Palo Alto for two days of on-site interviews. I did a bit of a road trip with my dad who by pure chance was freelancing in LA. He came over to the Bay Area for 2 days, we rented a fancy car and had fun.</p><p>The Valley was cool. While at 1 Hacker Way I saw Sheryl Sandberg doing a walking meeting across the FB campus with 5 of her reports. When I checked into the Sheraton there was a guy in an Instagram shirt standing next to me, he looked very happy. Instagram was bought for $1B a couple of months prior and the guy was now worth north of $50M but he still looked pretty normal. This was a different world. I also saw my first Teslas on the road and was stupid enough to put my actual current salary on some HR form.</p><p>Back home I got a good offer, negotiated a bit and got a better offer. IIRC it was 128k + 2x yearly 20k signing + stock, and for a 25-year-old engineer from Europe back in 2013 that was amazing. I still don't make close to this amount now, even though I probably could ;) In the Valley it was probably "meh" and that's why they recruited from overseas. But remember, I never expected to get an offer because in my head I wasn't good enough for the Champions League of the tech industry. So now I had to think. While thinking I discovered that:</p><p></p><ol style="text-align: left;"><li>I don't care about money beyond basic needs.</li><li>I'm very loyal and liked our mission at Mendix a lot.</li><li>I care about the environment. Flying back to Europe 2 times a year with Stephanie my wife and a brand-new kid did not sit well with me. We now have 3 kids and it still doesn't.</li><li>I don't like joining a winning team, I like a challenge and have a giant weak spot for the underdog.</li><li>I'm actually a pretty great engineer.</li><li>The H1B visa would not allow Stephanie to do any paid work. The H1B spouse club is real and that's a pity.</li><li>I want to work on something good and meaningful and still hate Facebook.</li></ol><p></p><p>I declined the offer. I felt bad about the super smart engineers and the recruiter for going through all this trouble (sorry Phil). I had made it clear from the start that declining was a very real opportunity. Thank you Zuck for sponsoring the free trip and the great learning experience. At Mendix, Roald told me he was very relieved I decided to stay. That was nice.</p><h2 style="text-align: left;">2012 - JUDO</h2><p>At Mendix I worked on the Cloud team, we hosted Mendix apps for customers. Hosting should be as easy as building. The Cloud/Ops/DevOps bunch was a tightly-knit team, as we were in the firing line together. With that I mean 24/7/365 uptime guarantees and you need to rely completely on your colleagues at all times of the day. It's something else. When I joined we were 4, when I left it was 12. Now it's 100. I'm in touch with a lot of these people to this day.</p><p>Every night the system created backups of the apps which admins could download over HTTP. The download consisted of the postgres dump, but also a lot of files from object storage. We could store ZIPs for every backup every night, but then the storage requirements would explode. Instead, we did incremental backups with rsync and hardlinks. Achiel wrote his BSc. thesis on this project for the sole reason of graduating within 10 years of starting college. If not, he would have to pay back his government study subsidies. That was fun. Hans was actually the mastermind behind the backup system. He's an incredible engineer and always built rock-solid stuff. Now this system was pretty cool, but the download needed to be a single zip file and that was difficult.</p><p>We built JUDO, the Java Upload Download Organizer. When getting a download request over HTTP, it assembled the zip file on-the-fly while walking over the files storage directories. This was a pretty complicated project and Xiwen, Frank and I needed a lot of help from the senior engineers (Hans and Achiel). Over the years we found obscure bugs and fixed most.</p><p>The streaming approach was very elegant. There was no temporary storage and no asynchronous processes. The fundamental problem was that the size of the file was not known at the start. The size and Content-Length header could not be predicted because of the compression. When a connection got interrupted, browsers were not sure whether the download was complete. This led to some problems. This is why most products use a "we are preparing your download" approach. Less efficient, but when dealing with backups it's important to know you have downloaded it completely.</p><h2 style="text-align: left;">2012 Decrypt.py</h2><p>With my new python skills I created a funny little program called decrypt.py. I had done some hard disk recovery for a friend, and found it a bit ironic that the end result was so bland. It just said "19783 files recovered". In movies it's always way more dramatic. My creation brings the drama back to boring things. It takes input, shows garbled text on the screen and slowly reveals the original content. You can view a demo on the <a href="https://github.com/jtwaleson/decrypt/" target="_blank">github page</a>.</p><p>It only took a couple of hours to build, but the main thing about decrypt.py is that it got me really hooked on Hacker News. I had discovered it a couple of months before, but had never posted anything. Now with this post I reached the top spot for a couple of hours (I think?) and saw the comments and upvotes coming in. It was very addictive. I've reached the top place a couple of times now and it's a rush every time. There have been very few days that I have not visited the orange website since 2012, and I did learn a lot from it.</p><div><h2 style="text-align: left;">2012 - Mendix WebModeler v0.1</h2><div><div>Mendix was (and is) a fantastic platform. Most Mendix developers use the Desktop Modeler / Mendix Studio Pro to create apps. Back in 2013 this was the only option. I had a different vision for the product. Maybe because I had no love for Windows apps, maybe because my stack was the browser, maybe because of the actual user needs. In any case, I wanted a web-based app creator. No installers, no friction. It would need a lot of difficult front-end engineering and a complete revamp of the codebase. Think of how much effort Microsoft had to put in Visual Studio Code. Add another re-architecture for client-server and you get the idea. No one within Mendix was crazy enough to try it.</div><div><br /></div><div>Being young and unhindered by wisdom or experience, I spent a couple of nights on creating v0.1. It was a PHP + jQuery based application that could view the microflows and models for a Mendix project. This logic comes from an MPR file. This is/was a SQLite database that contains all modules, logic and forms. I loaded the Mendix Cloud MPR file, put it on a password protected server and sent the link around to the CTO, CEO and some other people.</div><div><br /></div><div>I remember watching the live server logs, seeing them play with the prototype. At some point I saw an IP from the US. That was Derek, the CEO. Boy that was exciting. I had a chat with him a couple of weeks later, and if memory serves, he said something like this. "The web modeler is clearly the future. We need this but more urgently we need something like a sandbox environment so people can try Mendix for free and show their apps to their colleagues." We got there too, but that's a story for later.</div><div><br /></div><div>About a year later we started a dedicated team for the Web Modeler. I was not in there, neither was any code from my prototype, but I like to think I helped shape the vision and showed that it was possible to bring Mendix to the web.</div><div><br /></div><div>The Web Modeler needed a lot of serious front-end engineering indeed. Michel Weststrate was one of the main engineers and he created MobX (then Mobservable) in the process. It took years to mature, but nowadays the Web Modeler / Mendix Studio is a serious part of the Mendix ecosystem and I'm proud to have been there.</div></div><h2 style="text-align: left;">2012 - mxplient</h2><div><div>The Mendix core had three parts:</div><div>- The MxModeler (IDE)</div><div>- The MxClient (Powering the app in the browser)</div><div>- The MxRuntime (Powering the back-end and managing the database)</div><div><br /></div><div>I was in the Cloud team and used a Mendix app for the Cloud Portal. As we needed to automate a lot with external scripts, I needed an API in the app. Mendix had no REST functionality yet, and being Python fanatics we didn't want to touch XML WebServices.</div><div><br /></div><div>Then I realized the app already had an API. In the browser UI, I was doing all these actions I needed to automate, and those actions went to the Runtime as HTTP requests. Using the browser debugger I discovered the undocumented API that the MxClient and MxRuntime used. I proposed just making this API public, but the Mendix core teams were very against this. Who did this junior engineer think he was? Michiel Kalkman was the main engineer of the MxClient at this time. He explained that this was a proprietary API that could change with every Mendix release. Not that that happened, but it could. Theoretically. Every couple of years. He was an extremely good senior engineer who had a lot of ideas for improvements to the API. If you're a visionary, you can get disappointed because not all of your dreams come true, or take longer than expected. In practice the API was very stable.</div><div><br /></div><div>In the long term he was right of course, as some big releases had big changes to the protocol, but that didn't stop me. For our own use I created the "mxplient" library from the reverse engineered protocol. The P stood for Python. I used it for some small scripts, like newnode.py, or whenever I was playing around with something. One by one people started discovering the project. It was a tiny library, badly designed, and I had no idea with which Mendix versions it could work with. I think Michiel kind of appreciated the hacky way I did things. He liked Python too and had built a similar project in Ruby. I think it's the only Ruby code that was ever seen at Mendix.</div><div><br /></div><div>Around this time we had hired Daria (QA Engineer) whose job was to test the Cloud Portal. I once showed her the script. I blinked and then one year later she had built a giant automated test framework on mxplient. Oops. Then I felt guilty and started improving it. I don't know if it's still used, but this was a nice project and became my go-to solution if I needed to automate something with Mendix.</div><div><br /></div><div>Also around 2012, Michel Weststrate built the REST-Services module to provide a more standard and stable API to users. Years later, REST was properly added to the Mendix core itself. It's funny, Michel and I were both very productive hackers but we always had a different approach. Michel would engineer solutions as Mendix add-ons (taking quite some shortcuts). I would reverse-engineer and create workarounds completely outside of the Mendix ecosystem. Much later the "serious software engineers" picked up the pieces and made real products out of them. As Michel's work was available to the entire Mendix community, his work had a lot more traction and most got included in Mendix itself at some point.</div></div><h2>2012 - Access2mendix</h2><div><div>I'm not sure how this project got handed to me. Maybe we were a bit desperate to find new revenue, and in that search, someone had realized that a lot of enterprises used mini-apps built on Microsoft Access. Microsoft Access was installed on most systems because it was bundled with Office, and it allowed mildly technical users to build apps. Going through proper IT processes involved layers of red-tape so lots of people had built lots of solutions with Access. As I hated red-tape too I understood those people very well. They were what was called "shadow-IT". These apps were stored on some shared folder which only a couple of people knew about, and no one knew how to maintain it. Lots of processes were going through these "unofficial" apps and this was seen as a problem, because IT was not in control.</div><div><br /></div><div>With Mendix we thought we could do better. Our platform was more secure, more scalable and supported smartphones too. With our centralized admin tools, IT would be in control.</div><div><br /></div><div>We would target these companies directly and try to sell our platform. By this time Derek and Martijn had moved to the US and had started our Boston office. To blow US prospects away, they needed a Demo with capital D. Martijn was already amazing at demoeing Mendix, and built apps while the potential clients watched. It really was something to see. Here I learned that "the product will sell itself" is BS. You need great sales people, and at Mendix we had lots of them.</div><div><br /></div><div>To tailor the Demo to their new story, it would be great if they'd have a customer upload their old Access app, and get a Mendix app out of it within minutes. This somehow ended up with me. I believe Derek remembered my WebModeler v0.1 experiment and bypassing two layers of management, asked me if I could take a look at this "Access2Mendix" solution. CEOs will be CEOs I guess.</div><div><br /></div><div>What followed was the most productive weekend I have ever had. Normally working weekends is crap, there is a deadline and you don't really want to be working but you have to. Here there was no deadline, no pressure, just a very interesting challenge.</div><div><br /></div><div>It would be really powerful if Access2Mendix was built within Mendix, so that's what I created. It was 99% Java code, but no one would know that. I wanted to convert three things from the Access app:</div><div><ul style="text-align: left;"><li>the data model</li><li>the data</li><li>the logic</li></ul></div><div>The forms were kind of impossible to convert, so to compensate for that I created default admin forms, one per entity in the data model.</div><div><br /></div><div>You have to understand that although I was in R&D, I was a complete outsider to how the Mendix model worked. I used Mendix, but had no insights into the specification. In fact, there was no documented specification. It would take another couple of years before Meinte and Jos would create that. I had to reverse engineer <i>everything</i>.</div><div><br /></div><div>First I learned how to read an Access file using an open source Java library. Then I took an "example project" from Mendix and started manipulating the MPR file manually, inserting new modules, models, microflows and forms. I believe I was the first person to ever attempt this, and the Modeler crashed in weird ways whenever I made an error. The development cycle was super addictive. Add some conversion logic, upload, see the result within seconds. If it worked, go to the next functionality, if not, fix the conversion logic.</div><div><br /></div><div>I literally worked from Friday evening 7pm till Monday morning 8am and only slept 6 hours per night. At the end I had a Mendix app where you could upload an Access file and within seconds you got a fully functioning Mendix app + database back. Customers loved seeing their actual data directly in the new app. It made for a super impressive Demo.</div><div><br /></div><div>Unfortunately the whole Access conversion market was less spectacular than we thought and the project was abandoned after a couple of months.</div><div><br /></div><div>The main thing I got out of it was a reputation within our sales and management teams. In turn I gained a lot of respect for our sales people and made some new buddies within the company. A couple months later we had the first Company Kick-Off at Duinrell. At the party Derek started handing out awards. It was great to see each other's contributions and celebrate them. During his short speech towards the first ever R&D Excellence Award I suddenly realized, oh crap, he's talking about me! I had to get on stage and after the initial rush of introverted feelings I was very proud. I believe this project was one of the contributors to me getting that award.</div></div><h2 style="text-align: left;">2013 - Stackybird</h2><div><div>The year 2013 was tumultuous. We were doing lots of projects at work, we moved to a fancier office on the Gedempte Zalmhaven, and I was secretly interviewing at Facebook. My personal life was about to be upended too, as we figured out that Stephanie was pregnant in February/March. Coming home one Friday night there was a surprise pregnancy test on my desk. Two thoughts fired like rocket ships in my brain, in opposite directions. One was happy and excited, the other scared of the responsibility and the stress. My facial expression was switching between them for a couple of seconds. Then I realized the nice thing to do for Stephanie was to focus on the first rocket. She must have seen the hint of horror in my eyes though, and it makes sense. Any good parent should be at least a bit afraid about having kids and it is a giant responsibility to put them on this earth.</div><div><br /></div><div>Hugo was born in October 2013, and two weeks later I was flying to SFO for the interview at facebook. Thanks for allowing that, Stephanie. </div><div><br /></div><div>A small part of my responsibility as a parent was to give my new son the opportunity to learn programming. I had received that gift from my father, and I wanted to pass it to the next generation too. When he was a couple months old, I went and created Stacky Bird. A bit premature, but it was on my mind already. I expected him to learn coding around age 8. You can see Stacky Bird here: <a href="https://game.stackybird.com">https://game.stackybird.com</a> .</div><div><br /></div><div>Stacky Bird was a combination of three of my interests:</div><div><ul style="text-align: left;"><li>teaching my kids programming</li><li>creating a programming language from scratch</li><li>the "meme-games" Flappy Bird, 2048 and SpaceChem that were popular in 2013/2014.</li></ul></div><div>The game allows you to solve problems by giving a bird directions. The initial version was super simple. After some time I realized that it was actually a programming language, and with a bit more features is probably Turing-complete. We got two more kids, Suzanne and Louise, twins, in September 2015. When the kids were 7, 5 & 5 I did a thorough rewrite of Stacky Bird in VueJS and they could actually play it. They liked the minecraft coding on <a href="https://code.org">https://code.org</a> a lot better though.</div><div><br /></div><div>I'm not a helicopter parent who's hyper-involved with the kids, but still, kids take up a lot of time. My energy levels dropped tremendously and it would take years before I got close to the 2011/2012 levels of productivity and energy. The hobby projects, hackathons and weekends of programming disappeared for 90%. Most of the projects you see in this post were done during working hours. I felt held back compared to colleagues who were able to power through weekends, especially in the startup I went to in 2018. Now, in 2022 I finally feel like I am getting back in the game. Sometimes when I have kids duty, I nap on the couch and my kids run around the neighborhood. So much freedom compared to the diaper phase!</div><div><br /></div><div>I was 25 when I got my first kid, and 27 when I got the last two. My advice for past-me would be to start a year or two later, but not much more. I love being a young parent and if the kids leave the house around their 18th, I'll be only 45. Again there's two thoughts. I'm looking forward to the freedom and travelling with Stephanie, but I'm also scared of the empty nest.</div></div><h2>2014 - Sandboxes</h2><div><div>Derek had a dream for the Mendix trial and he kept talking about it. Sandboxes. You could already try Mendix for free, create an app and run it on your own PC. But you couldn't share your apps to your colleagues. We needed a trial environment in the cloud. Derek was an excellent CEO because he had a clear vision which he kept repeating. He said what users needed and didn't micromanage the solutions. For all I know the higher-ups might have experienced a different side of him, but being at the bottom of the food chain this was my perspective. I have tremendous respect for him.</div><div><br /></div><div>We could not have thousands of free apps running on our infrastructure, costs would go through the roof. Serverless didn't exist and the Mendix runtime was quite heavy. The only remaining solution was what Heroku did: stop the app when not active and resume it when traffic came in. I had learned a lot while building the Mendix Cloud and during the interview process at Facebook. Armed with this knowledge and Derek's vision, I had a spark and realized how we could copy Heroku's feature. We could send nginx traffic to a mini app that would start up the main app. When started we would rewrite the nginx config to send traffic back to the main app and trigger a reload. I spent the weekend thinking and tinkering, and at the end I had a prototype of an app that could be resumed when traffic came in. Stephanie didn't see much of me that weekend. It was awesome.</div><div><br /></div><div>On Monday I was like an overly enthusiastic kid and told everyone that we had the final building block for a solution. Somehow it got on the roadmap. I had no idea how that roadmap process worked, to me it was all energy, inspiration and magic. Actually that building block was just one tiny step for the Sandboxes release. Because it was the only "magic" part I focused on that, but there was so much more. We still needed:</div><div><ul style="text-align: left;"><li>Cloud infrastructure that could support dynamic re-assignment of apps to virtual machines.</li><li>A "last request" counter in the runtime.</li><li>A deploy button in the Desktop Modeler.</li><li>A "your app is resuming" page.</li><li>A runtime license for the free apps.</li><li>An SSO service for inviting your colleagues.</li><li>MxBuild, a cloud based compiler for the apps (not sure why it was needed, but we did build it around this time).</li></ul><div>The MxBuild project was interesting, Benny and I had built it on a research day once, and it was the best pair programming experience I've ever had. He was building the Mono executable, and I built the python code that called it. For some reason we were so well attuned to each other, that we would see the other side of the API being typed on the other person's screen, and typed the other side on our own screen. An error popped up on my PC and Benny would fix it on his without a word. With hardly any explicit communication the entire thing just spontaneously came into existence. Two minds working as one, it was really beautiful.</div></div><div><br /></div><div>But back to the sandboxes release. We also needed more hardware! The co-location data center in Amsterdam was getting full so we needed to add a couple more servers with a lot of memory. For that we needed the CFO to sign off on a couple $100k in infrastructure costs. That was difficult because we knew we were going to the Cloud soon. Derek had been saying it for years. Why did we need more money now for hardware? Well, the "real cloud' would not be ready for about two more years. Thanks to Hans' and Pim's engineering we had a super cost efficient infrastructure in co-location DCs. Bert (the Head of Finance) asked us some questions and explained some financial models to us, that was enlightening. We figured this was actually a great investment and AWS was going to be very expensive. Oh well. It would still be making us money.</div><div><br /></div><div>It's pretty cool how we engineered this mega feature with effort from so many R&D teams. IIRC the launch went great, but quite quickly the VMs we used were filling up. Within a month or two I needed to hack a script to delete unused sandboxes. We kept doing that until the launch of Free Apps, about a year later. The infrastructure behind Sandboxes was a stopgap solution until we had Mendix Cloud v4.</div></div><div><h2>2014 - cf-mendix-buildpack</h2><div><div>The 12-factor app manifesto made an enormous impact on the world of cloud. If you didn't do 12-factor apps, you were stuck in the stone age. At Mendix we were stuck in the stone age, and we wanted out.</div><div><br /></div><div>I believe it was in 2013 that Johan, the CTO, spent a weekend on getting Mendix to run on Heroku. He was still close enough to the Java code that he could compile his custom runtime for this purpose. I think this is the last serious programming I saw him do. He's a fantastic CTO and unlike me learned to let go of coding.</div><div><br /></div><div>We never pursued Heroku again, but a year later Mark Rogers, who was leading Business Development, discovered Pivotal and their Cloud Foundry offering. This was an enterprise platform that targeted the same market as us. Cloud Foundry was Heroku for the Enterprise, and it was awesome. It would take us out of the stone age and into the 12-factor scalable future.</div><div><br /></div><div>If I recall correctly, Johan asked me to try it out and to see if I could get Mendix to run on it. I was skeptical, but once I got access I gave it a spin. Being a Product Manager I now had a bit more freedom to spend my time than the daily scrum rhythm. I took the m2ee-tools code that Hans had written, wrote a tiny bit of glue code and hacked together a very ugly buildpack. Hans hated it because I took 12329 shortcuts. It worked though. You can see most of this history here: <a href="https://github.com/mendix/cf-mendix-buildpack">https://github.com/mendix/cf-mendix-buildpack</a> . The initial commits were too ugly to keep so I rebased it. Initially it ran on both Heroku and Cloud Foundry.</div><div><br /></div><div>"Production Ready" is a relative term. We were going all-in on the Cloud Foundry ecosystem and needed to run on IBM Bluemix, HP Helion and Pivotal Cloud Foundry. I think I became "mister cloud foundry" within Mendix, at least Erno started calling me that. Within a couple of months we flew to Barcelona for HP Discover, and I saw Johan on stage with Meg Whitman. He was talking to 5000 people about how easy it was to create a buildpack. "It only took us a couple of days!" He's a great showman, even with the Dutch accent ;)</div><div><br /></div><div>The buildpack allowed us to go 12-factor. It needed major changes to the Mendix Runtime to remove state and scale horizontally which took about a year. Years later, in 2018, we had the first apps that really could not work without horizontal scaling. We were running an app that powered the "huishoudbeurs". A big event in the Netherlands that would see huge spikes in traffic. It ran on Cloud v4 and was very horizontally scalable. In my memory there were very few issues, and it was a proud moment for me.</div><div><br /></div><div>The buildpack also allowed us to seriously partner with Pivotal, IBM, HP, SAP and GE. It gave us a seat at the table. Our Private and Public cloud propositions were much stronger with the Cloud Foundry play. Nowadays Cloud Foundry is not doing so well, and the Kubernetes beast became the standard platform that CF wanted to be. In my view CF has a much better developer experience than K8s and to me, that's what matters.</div><div><br /></div><div>Although I was no longer an engineer but a Product Manager, the buildpack was clearly my project. It was small compared to the effort we put in m2ee-tools or the Mendix Runtime but it was crucial. It's my #1 project in terms of business impact. Every day, thousands of Mendix apps start running using my code. Although better engineers have probably refactored most of it, I'm immensely proud of it.</div></div></div><h2 style="text-align: left;">2015 - InstaDeploy</h2><div><div>When we launched "Free Apps" I was pretty proud. It was basically "Sandboxes V2 + WebModeler". Under the hood we had this whole new architecture with all kinds of clever "cloud-scale" solutions. It turned out that users were not so happy. Every time you changed something in your app you had to deploy it. We wanted people to use Free Apps so we hid the old "local deployment" option. The Free Apps deployment service was buggy and slow. Local deployment used to take 10 seconds. The old sandboxes took 1 minute, the Free Apps took 4. On bad days it took 8.</div><div><br /></div><div>The cloud team had launched the deployment service without any SLAs or basic explanation of the limitations to the other teams. A bit immature. The WebModeler and Modeler teams just had to use it and see how it worked. In testing it was already very flaky, but under production usage it was really bad. We thought we could get away with it because in the cloud, the rubber hits the road and we had failing stuff all the time. The other teams had stable unit tests etc. Not us. We thought that was cool. Now the ones who got most of the flak from management was the WebModeler team. They had all the visibility and the deploy button was in their product. They felt helpless.</div><div><br /></div><div>Me and the cloud guys were not very open to helping them out. I was under a lot of stress to launch cloud v4. In the meantime we were scaling cloud v3 and needed new hardware every couple of months. This took most of my time and mental energy. I also could not justify the focus on our Free Tier while our paid production workloads were in danger. Sorry for having to deal with that, Erik vd P., Michel, Kishen, Daniel D. and Arjan B. . After it got really bad we took part of the blame and we started figuring out how to optimize the deployment time.</div><div><br /></div><div>Then we had the annual Company Kick-Off in Noordwijk (the Mendix CKO). I ran into Derek, and said that I was confident we could get it to two minutes. His response was very Steve Jobs-like: "Two minutes? Why should it take any time at all? It should feel instant! If I make change I want to see it right away!". F*ck. He was right. We needed to rethink our entire approach.</div><div><br /></div><div>I've never had a clearer call to action. I don't know how, but I managed to convince Johan that we had to put the top hackers from every team in one room for two days. This was Benny for the Modeler/MxBuild, Michel for the WebModeler, Xiwen and Daniel for Cloud, and maybe some more people. Let me know if you were there. I was there too, "managing". Because of Derek's speech I was fired up. Everyone else was happy we could finally fix the deployment problems. We all felt like we were on a mission from God.</div><div><br /></div><div>We added some timing logs, broke down the deployment process and started optimizing our own separate parts of it. We started at 4 minutes and had ideas on how to get to 2. I realized we had to take ugly shortcuts to get beyond that. Instead of sending the deployment package to the deployment service, it had to go straight to the running container. This was very unholy and the 12-factor-app gods did not approve. I thought of Bill O'Reilly. "Fuck it, we'll do it live". We modified the buildpack to include a compiler on hot standby. When it got a deployment package on a separate secret HTTP path, it would trigger the compiler and reload the app. Benny built the mxbuild server. Xiwen got it to run in the container. Michel optimized the webmodeler so it could create a deployment package in 10 seconds instead of 20.</div><div><br /></div><div>The time kept dropping. We were at 40 seconds. Benny optimized some more with pre-warming the build cache. Michel said "a-ha" and got package creation down to 0.1 seconds. At night Xiwen or me finally got mxbuild to run. On the second day the end-to-end unholy deployment cycle was working. Not in 2 minutes, not in 1 minute, but in 2 seconds. Holy shit.</div><div><br /></div><div>It made for a great demo, a great story and when it worked, a great product. It never got super reliable, but boy was it fast.</div></div><h2 style="text-align: left;">2015 - mprtools</h2><div>TODO</div><h2 style="text-align: left;">2015 - Mendix2java</h2><div><div>Roald called me up one day and said we had a problem. We were about to close a major deal with a large company that I can't name here, but the sales process was stuck at the last stage. Roald was co-founder and pretty important and he never called me, so that was interesting.</div><div><br /></div><div>He's an interesting character: driven, business focused, and very smart. He's unbeatable at the interplay of tech and sales. He's very confrontational, so I had to get used to him in the first couple of years, but after that I deeply respected him. He's confrontational because he cares. I think the respect was mutual because he called me in this crisis, and we worked together quite a bit when I was a PM.</div><div><br /></div><div>This almost-customer was stuck on vendor lock-in. Building things on any platform is risky because it's expensive to migrate off of it. The more value a platform provides, the more it costs to get out of it. Mendix is no exception. Our sales people were able to convince most customers that this was not unique to Mendix and that it was alright, but here they didn't budge. Roald knew my reputation for getting sh*t done and that I had done all these crazy projects in the past, like mprtools and Access2Mendix. So, he called me up and said we needed a demo of exporting the logic in a Mendix project to a "normal language like Java". We'd need it Friday. Today was Tuesday and I was flying to a conference that was happening on Wednesday and Thursday. Time was tight to say it mildly.</div><div><br /></div><div>"What do you need to get it done?"</div><div><br /></div><div>In times like this, having lots of people doesn't help. A group of 2 to 3 people that already work together extremely well could work. I knew that I needed David. Done. He was on the team. David and I already worked really well together, being good hackers and now both Product Managers. We had a good vibe. He's very clever and had good theoretical knowledge about language fundamentals and transpiling. I don't, but did have all this previous hands-on knowledge.</div><div><br /></div><div>We knew this prototype was going to be a demonstration that an export to a different language <b>could</b> be done. We were not selling the tool to the prospective customer. We were showcasing that the high-level abstraction in Mendix allowed exports like this. So we were off to the races. Using mprtools we created export-logic for the Domain Model to SQL table creation statements. Not super hard. Then we started extracting the logic in Microflows to Java classes. Ok, this got complicated quick.</div><div><br /></div><div>In Mendix microflows, every arrow in the diagram is basically a go-to statement. We had to eliminate the go-to's and migrate them to for-loops, if/then/else constructs, etc. . There was some research from the 70s on this topic but it was useless right now. We decided to build the easy cases first and drop down to a "TODO" statement for too complex cases. The people working on the export would have to figure this out for themselves. When I was boarding my plane we had gotten quite far and David worked on it for some more days. At the end there were only very few cases left that the exporter could not handle.</div><div><br /></div><div>I wasn't there, but apparently the Friday demonstration went great. "If you can do this within a couple of days, it's clear that it's possible and you have a great solution for vendor lock-in." Huge parts of the code were still missing, and the secret ingredients of the Mendix Runtime could not be included in the export at all. How useful was this actually? But, we did a lot better than our competitors and we got the deal! If we were in Pre-Sales, David and I would have probably gotten a fat bonus, but we were in R&D. Sometimes the intellectual challenge is its own reward. It's certainly a cool story.</div></div><div><h2 style="text-align: left;">2015 - Certinator</h2><div>My brain is different from that of most people. This is a bit exaggerated, but I either understand something completely or I'm very confused. A coworker recently said that I somehow "know what I don't know". This is a blessing and a curse. A blessing because when I get it I am able to see bugs or solutions in the blink of an eye. A curse because I'm kind of useless and doubt everything until I understand a topic 100%.</div><div><br /></div><div>Both `git` and SSL/TLS were topics that I didn't get for a long time. When I learned the git commands I was useless, but once I learned the data structures I became a git expert overnight. It's a beautiful idea brilliantly executed. SSL/TLS was the same.</div><div><br /></div><div>In the Mendix Cloud, customers needed to add custom domains for their apps. As we were enterprisey, this had to be HTTPS but no low-code developers understood how that worked. Once I "got it" I was able to help them out and debug things quickly. I built a small tool "certinator" for this purpose. If you pasted your certificate chain it gave you feedback or autocompleted it. The tool never got far, but building it was a nice journey.</div><div><ul style="text-align: left;"><li>Certinator needed to autocomplete intermediate certificates.</li><li>For that I wanted to find all common intermediate certificates in the world.</li><li>For that I needed to connect to millions of popular websites and look at their certificate chain.</li><li>For that I needed to find a list of millions of websites.</li><li>For that I could use the CommonCrawl dataset and extract URLs.</li><li>For that I set up a huge EC2 instance in the same region as the CommonCrawl data. I optimized and processed 10TB of data on it within a couple of hours.</li><li>Then I connected to all domains and collected the certificate chain with a tiny `golang` program.</li></ul></div><div>Watching the machine crunch away the data on 32 cores, and saturating the 10GBps network connection for hours on end was really incredible.</div><div><br /></div><div>When it was done, I created this blog post: <a href="https://blog.waleson.com/2016/01/parsing-10tb-of-metadata-26m-domains.html">https://blog.waleson.com/2016/01/parsing-10tb-of-metadata-26m-domains.html</a> . I posted it with a rather clickbaity title on Hacker News and got to #1 for a couple of hours. My blog post got about 50k hits in one day. That was cool.</div><div><br /></div><div>We used certinator within Mendix every now and then to debug some certificate issues. My friend Sebastian in the CloudOps team took over that part of the job and I think he used the tool for a couple of years. You can find the source code here: <a href="https://github.com/jtwaleson/certinator">https://github.com/jtwaleson/certinator</a></div></div><div><br /></div><div>A couple of years later I got a message from a startup in San Francisco. They wanted to extract data from the CommonCrawl archives too and had seen my blog post. I was excited and offered to build it for about 3k. It would probably take two days to build, max. They accepted right away, so I was probably too cheap ;) Extracting their data was much more compute intensive, so I went with an ec2 spot-instance fleet. Running the entire archive took a couple of hours with about 100 large machines. It was incredible to see. If you want something like this, call me anytime because I enjoy this kind of work.</div><div><h2>2015 - buildpacks for Docker</h2><div>As I got more familiar with buildpacks and docker, I figured out that we can use buildpacks to create runnable docker images. Not just droplets that you can only run in Cloud Foundry, but plain old docker images. It only took about 100 lines of bash! I still love the elegance of it: <a href="https://github.com/jtwaleson/buildpacks-for-docker">https://github.com/jtwaleson/buildpacks-for-docker</a></div></div><div><h2>2016 - Mendix Cloud v4 - Part 1 - How it got started</h2></div><div><div>We had been building a big new cloud for a year or two, but we did not have a name. I was the Product Manager for "it". Should we call it the Real Mendix Cloud? The Cloudy Mendix Cloud? The Next-Gen Mendix Cloud? Or a brand name like e.g. Heroku, HP Helion, IBM BlueMix were doing. After brainstorming with Roald it dawned on me that this was actually our 4th generation cloud offering. Why not simply call it v4? Customers had always had lots of confusion on where their apps were hosted. They called it "the Mendix Cloud", the "Achiel Cloud" or the "Hans Cloud". If we now said "you are on v2" or v3, and you need to go to v4 because of X, that'd be a pretty simple explanation.</div><div><br /></div><div>I really liked the v4 name and pushed for it. No one had a better alternative so it was done. In the end the name worked well and I was very proud of my find. So how did we get here? A quick recap.</div><div><br /></div><div>It had been a long-time ambition of Derek and Johan to become a cloud company. We told customers that they could create their software faster, and maintain it better, but deployment and hosting was still as slow as always. So a couple years before I joined, something happened that started our hosting adventure. We were already running an exchange server, a wordpress website and some other standard IT things when a customer was going live with a big Mendix project. They had a hard deadline because of compliance with a new Dutch insurance law. The only problem was that their IT team could only deliver a new hosting server in about a year. The deadline was end of the month. Not good.</div><div><br /></div><div>I was not there for any of this, but the story I heard is as follows;</div><div><br /></div><div>The Mendix guys, creative sales people as always, said "we can do this for you!". They went to their sys admin (Hans) and asked if he could host the app. "Yes we can!". It was a cool engineering challenge, and so customer hosting started. Within a year or two there were all kinds of apps running on virtual machines on colocated hardware. In the beginning there was no budget for anything so the servers were really cheap supermicro machines and the switches were cheap as hell. Disks crashed and at some point the entire SAN was running without any redundancy. Exciting times. We now call this "Mendix Cloud v1".</div><div><br /></div><div>After some more years there were 3 Ops engineers with about 100 customer applications and they had been more or less "standardized" to a blueprint. The hardware had been expanded, the team was bigger (Frank and Mark joined). The old hardware was phased out and VMs moved to new infrastructure. Things were looking good. But then C-level started talking about Cloud, and that this "hardware thing" was not cloud. Something was brewing.</div><div><br /></div><div>The requirements for the applications were increasingly "enterprisey". Uptime requirements were very high, VPN access was sometimes mandatory, real-time failovers had to be built. We were building mission-critical stuff. This was "Mendix Cloud v2". Mostly standardized hosting but also some mission critical systems with customizations. There were also still some non-standardized "v1" services running, that slowly had to be migrated to v2.</div><div><br /></div><div>At this time I joined, as described above in "newnode.py", and Achiel and I started building the "Mendix Cloud". This was when we officially started using that name. Now the main thing about "Mendix Cloud" was to take the hardware blueprint, glue it together in an admin portal (built in Mendix) and let customers manage apps instead of servers. Ironically the Mendix Cloud Portal ran on Cloud v2, which made sense, because we had to be able to start it over SSH if it was down. Of course with Achiel and my limited skillset, we should not host any mission critical stuff yet. So we took on new simple apps, and also started migrating some apps from "v2" to the brand new "Mendix Cloud", retrospectively called "v3". Sometimes it turned out that apps had mission-critical requirements and we had to move them back to v2. Or the other way round. Fun.</div><div><br /></div><div>We ran the "Mendix Cloud" not on our own hardware, but on cloud providers that were less reliable and much more expensive than the "v2" setup. It was officially "cloud" though because we did not run the servers ourselves, and the costs were OpEx instead of CapEx. We were at a crossroads. Cloud v3 was growing big and expensive, and not capable of serving mission critical stuff. Hans said if we go cloud, we should go really cloud. That basically meant a 12-factor platform and architecture. Now we did not have a 12-factor platform, or even a runtime that could run on it, so we would have to build a lot. That was for now a no-go. Instead we decided to move v3 to our own infrastructure which was more stable, cheap and could support mission-critical apps. We went all in on more expensive hardware (HP ProLiant DL380, NetApp SANs and Cisco switches) and built about 5 full racks worth of equipment in 3 data centers in NL. One by one we moved apps off of Rackspace and Linode and onto our own server environments.</div><div><br /></div><div>As a medior engineer I had not so much to say about these strategic choices, but by this time we started getting the first Product Managers in the organization. Andrej was the first Product Manager, and after that I immediately applied. Everyone was very much like "wtf I thought you were a nerd that only likes programming". I was told to reconsider and come back in a week. A week later I came back without a changed mind. For some stupid reason I was really determined to do something else than programming. I wanted to have a say in the strategic direction of the product. Johan said he would have to think about it, as Product Managers are typically kind of senior and I was 26 or so. He was 3 years older and CTO since 25 so he always made a point that age did not matter. He managed to convince some people and it worked out, I switched to PM without any additional pay, training or clear responsibilities. Fun times!</div><div><br /></div><div>I knew Johan and Derek wanted to go to the "real cloud", go global and support massive scale. We had sort of bet on Cloud Foundry already, so we started experimenting with it and see if we could run a Cloud Foundry cluster ourselves. I don't recall that that choice was ever expliticly made, so it probably was made higher up. As PM I was not sure what my responsibilities were, but I assumed my "mission from God" was to get us onto a real cloud using Cloud Foundry. Daniel, Xiwen, Frank and Riccardo started building and playing around with Cloud Foundry and AWS a lot. Once we had some apps we figured out how to build service brokers. Now we knew what we had to build, but it would be a lot.</div><div><br /></div><div>As PM I was wearing an insane amount of hats, and I loved it. I was doing quite some cloud v4 technical architecture with the team, customer service escalations, joining CSMs on business review calls, managing v1 to v2 to v3 migrations (badly), convincing customers to not build new VPNs, thinking of a pricing strategy for cloud v4, figuring out a staged launch plan and finally creating a migration plan from v3 to v4. All this while v3 was still growing about 50% to 100% year over year and infrastructure was running into new limits all the time. We also needed marketing materials, analyst demos. I had to manage a lot of customer escalations and I did a lot of on-call 24/7 support. I was not supposed to be doing on-call duty, but I had the skills, could use the extra cash and didn't want to burden the team which was already stretched very thin. My to do list was overflowing, I was in over my head, it was pretty stressful and rough, but I'm happy to say that we managed in the end.</div></div><div><br /></div><div>In part 2 we look at how we launched Mendix Cloud v4.</div><div><br /></div><div><h2>2016 - Packing all mxruntimes in git</h2><div><div>One of the things that made the buildpack slow, was that it had to download and unpack the whole Mendix Client and Mendix Runtime when building the container image. Over time these got dependencies got bigger and bigger. The images were bloated and needed time to transfer over the network. We needed a way to add a "cache" to our Cloud Foundry clusters, so that the buildpack could just copy the files from disk. We would not even have to include the dependencies in the final image. That would be a lot faster because the apps themselves were usually pretty small.</div><div><br /></div><div>Storing all the hundreds of runtime versions in the cache of every server was going to take up a lot of disk space though, we needed something more clever.</div><div><br /></div><div>I realized that most versions were minor updates and that a lot of files were shared between multiple versions of the runtime. I went ahead and created a git repository that stored all of the runtime versions as a tag. You can see the archive here: <a href="https://github.com/mendix/runtimes">https://github.com/mendix/runtimes</a> . This repository would be about 1.5 GB and stored 210 runtime versions of +/- 500MB each. Excellent compression!</div><div><br /></div><div>We would save this repo in our own fork of the cflinux2 base image in Cloud Foundry, so it was already present on all machines in the cluster. Using the copy-on-write mechanisms in Cloud Foundry, we could even do "git fetch" if the app used a brand new Mendix version that was no stored in the cache yet. This was pretty clever. The downside was that it depended on github.com for building apps. Not ideal, but solvable. Another downside was that it would give smart people early notice on when we were releasing a new Mendix version, as they could see the new runtime being pushed.</div><div><br /></div><div>For some reason we stopped using this approach in early 2018. I don't remember what the team replaced it with.</div></div></div><div><h2 style="text-align: left;">2016 - The too clever scheduling service</h2><div>In Mendix Cloud v4 we only wanted to use fault-tolerant services. So no single VMs, but "services" that were "web-scale", "clustered" and "horizontally scalable". Most services we created were request based. So a request would come in, and the service would respond. Simple. We built everything according to the 12-factor app architecture and ran apps on Cloud Foundry. On the AWS side we used lambda and SQS. This was our toolbox for the new architecture.</div><div><br /></div><div>One service that was not request-based was the backup service. Every night we had to create backups. Users didn't trigger this backup creation, we had to trigger it ourselves. If you use a traditional VM, you'd set up a cron job and you're done. In our new world this was somehow considered bad, but we had no tools in our toolbox to schedule events. AWS Lambda has it now, but back then it did not.</div><div><br /></div><div>For the backup-service we set up an app in Cloud Foundry that would listen to an SQS queue. If we put a "create backup for app X" job on the queue, the backup service would pick it up and create a backup. This makes sense and is a pretty good architecture. However, we needed something to put these jobs on the queue every night for all apps. Xiwen and I came up with something that was supremely clever. We would put another kind of job on the queue. That job was "run all the nightly backups for <DATE>, and do this at <TIME>."</div><div><br /></div><div>If the app would see this job, and it was currently past <TIME>, it would trigger the nightly backups AND submit a job for the next day. If it was not yet <TIME>, it would hold the job for about a minute, and then put it back on the queue. The app kept kicking the "can" down the road. Ad infinitum. </div><div><br /></div><div>We thought this was clever. If anything ever happened to "the can", there would be no more backups, not this day, nor the next, never. Full of hubris we thought this would never happen. Our team started hiring new people during this time, and Xiwen and I proudly told them about the design. The responses were kind of mixed. When we told Emir he was very impressed by our engineering prowess. When Hans T. came in, he was like "what on earth were you guys smoking".</div><div><br /></div><div>We went ahead anyway and launched. I remember we had to "insert the magic packet" into the queue by hand to kick-start the process. What happened next was kind of funny. On some nights we had no backups. On some nights we had two, sometimes three. Sometimes half the apps had a backup. We had no idea what was happening. Hans T had been right, the system was complicated, hard to debug and super fragile.</div><div><br /></div><div>Because we launched on our free tier we had some time to "screw around", and we got it under control eventually. I don't remember how, but I remember we put duct-tape on duct-tape to make it reasonably reliable. In hindsight we should have been less principled. We had all these super reliable VMs from Cloud v3, and adding a simple cron job to start the backups in Cloud v4 would have been trivial. We didn't do it because we shouldn't "cross the streams". The KISS principle is really important when engineering production systems. "Keep it simple stupid." Better have a small ugly wart than a big one with a lot of make up plastered over it. It was a good lesson for all of us.</div></div><h2 style="text-align: left;">2017 - The Reaper</h2><div><div>We launched Mendix Cloud v4 by starting the new Free Apps tier on it. These apps without SLA allowed us to take more risks and learn to operate Cloud Foundry. The Mendix Free Apps cluster had enough RAM for about 100 concurrent apps. It could scale up and down, but we wanted to keep the costs reasonable.</div><div><br /></div><div>An app would run as long as there was HTTP traffic to it. If an app did not have any traffic for 30 minutes, we killed it. If an HTTP request would come in, we'd serve a "loading" page while the app was spinning back up.</div><div><br /></div><div>We had a small app (The Resumer) that would catch all traffic for apps that were not running & serve the loading page. I believe Xiwen wrote most of it, but my memory is failing me. It would fire a request to the CF API to find and start the app. The Reaper was its counterpart. It would talk to the CF API, list all running apps. Then call the admin API of the app to see when its last request was handled, and kill it if > 1800 sec. For some reason, The Reaper stopped running once we had more than 15.000 (?) apps in the Free Tier. The cluster started misbehaving badly when out of capacity. We could not figure out why or how, but needed a fix.</div><div><br /></div><div>When running the same code on my workstation everything worked fine and the cluster went back to normal. So, you can guess what I did. Against all proper engineering practices, I ran "The Reaper" from the workstation under my desk with a cron job for a couple of months. I was a Product Manager and should not be touching code or infrastructure at all, but I couldn't help myself. Later on, Daniel vD (now also a CTO), found that there was a bug in the CF API logic. Without admin credentials it would make a query that scaled O(n2) with the number of apps. Admin credentials didn't need the extra security protection for the query so that's why that was fast. As we were the only idiots in the world running Cloud Foundry with 15k stopped and 100 running apps, no one else had run into this.</div><div><br /></div><div>Thanks to my friend Daniel you can read the issue here: <a href="https://github.com/cloudfoundry/cloud_controller_ng/issues/1272">https://github.com/cloudfoundry/cloud_controller_ng/issues/1272</a> . When he documented the issue we were apparently at 85k apps. We were growing fast! The Reaper was a pretty cool name for a pretty cool tool.</div></div><div><h2>2018 - Mendix Cloud v4 - Part 2 - How we launched it</h2></div><div>TODO</div><h2 style="text-align: left;">Part 4 - CTO @ easee ('18-'23)</h2><h2 style="text-align: left;">2018 - The easee Wizard Engine</h2><div><div>After almost 7 years at Mendix I got a bit restless. My product Mendix Cloud v4 had launched, the big engineering behind it was done and the endless on-call duty was tiring me out. I felt ready for something new. Being addicted to Hacker News I badly wanted to join or start a real startup. A lot was happening. My friend Erik was going to start Soulpicks. Mohsen had some idea with luggage tracking. Rik left for a crazy eye testing startup. Dennis was starting Blockbax with Roy. I talked to some of them about starting something together, but nothing panned out. I had a family and a mortgage, and being the sole breadwinner my personal burn-rate was quite high.</div><div><br /></div><div>Then I got a message from Rik, who was now the lead unicorn engineer at easee. "We're looking for a CTO. Do you maybe know anyone?" Well, no, but I was interested! A couple of months, sleepless nights and a thorough interview process later, I was the CTO. We were a bit worried about the non-compete with Mendix, but it turned out fine. These things are not really enforceable anyway. This was a hip startup in Amsterdam, exactly what I wanted. Getting in I realized we only had a month or two of runway. The only way to raise some funding was with a CTO in the team, and we needed the funding to pay me. A classic catch-22.</div><div><br /></div><div>Even before I started full-time I joined for a couple of investor calls, made my first hire (Francesco) and had an epic party after the quarterly kick-off. I'm still banned from dancing on stage at the Chin-Chin club in Amsterdam. This was all new and super exciting to me. Yves, the CEO, was new to startups too, but having an MBA under his belt and a lot of corporate experience was a lot more experienced than I was.</div><div><br /></div><div>I took 0 days off between Mendix and easee. In hindsight that was not healthy, but we had 3 urgent problems to solve.</div><div><ol style="text-align: left;"><li>Funding.</li><li>Scalability. V1 of the eye test would crash with about 10 concurrent users. There was a spike of users after we got on RTL Nieuws and the database did not look pretty after that.</li><li>The performance of our cylinder test. A clinical study was underway in the UMC Utrecht, and things looked good, except for the cylinder test. We had a new one but needed to implement it.</li></ol></div><div>We divided and conquered. Yves was naturally on funding (as he has been ever since ;) ). I was on scalability and re-architecture. Rik and Francesco were a couple months more experienced in eye testing and took on the cylinder test. They built this on the new architecture that I was building.</div><div><br /></div><div>The easee online eye exam is fundamentally a big wizard (plus lots of clever algorithms in the back-end of course). It was implemented as a state machine in the database. Every click went through the state machine in the back-end, a classic MVC in Laravel. This was slow and did not scale because the database architecture was a mess. With a client-server architecture we could never get to sub 100ms responsiveness. I decided to go for a Single Page App built with Vue, with most of the logic of the wizard in the front-end. I had no experience with Vue, but loved its pragmatism. Now I had to figure out how to create a simple engine for displaying the screens in the correct order.</div><div><br /></div><div>We needed to show screens one after another, and depending on the input of the user, go through a different path in the wizard. We could simply point the "next" button to the next screen. But some screens were re-used, so that was a no-go or we'd get a loop. I felt like the code would get messy, but there had to be a clean solution. It took weeks of banging my head against the wall, but then it clicked. I learned how async/await worked (I never understood it before). I realized the logic of a wizard is just a classic program. Each screen has input (the props) and output (whatever the user selects). If we wrap this in a promise we can "await" the user's action. Instead of "awaiting" a database or remote server, we await the action of the user. I thought this was extremely clever.</div><div><br /></div><div>So that's what I built. A small engine of about 100 lines of code that can show a screen, await the user's action on it and return the output to the main program logic. It can serialize the data and store it in session storage so that you can safely reload the browser. The serialization also allows the state to be sent to another device and continue there. I can't share much more details, but it's really cool. </div><div><br /></div><div>This simple engine allowed the developers in the team to just work on the screens and keep the codebase clean. It also made the flow of the eye exam easy to understand, as it's just a program with functions, output and variables.</div><div><br /></div><div>We quickly rebuilt the exact same product as v1 on the new engine. Then we added the new cylinder test and launched the second part of the clinical study with it. Yves managed to raise a good seed round with Nimbus Ventures. All of this within 4 months of me joining. In November 2018 we put v1.5 of the product live with the new engine. Since then it has powered hundreds of thousands of eye tests and had almost no bugs. I am very proud of the easee wizard engine.</div></div><h2 style="text-align: left;">2019 - The easee Diff Viewer</h2><div>Medical Devices need an IFU document. The "Instructions For Use". It's called the User Manual in the normal world. The consumerisation and digitalization of healthcare blurs the contrast between the Medical Device world and the normal world. When is the last time you looked at the user manual for an app on your phone? Do most apps even have one? Probably not. The User Interface should be be self-explanatory. Why would it be different for a healthcare app?</div><div><br /></div><div>Having an IFU is kind of a ridiculous requirement if no one will ever look at it. As far as I know, even the regulators won't take more than a cursory glance for low-risk healthcare solutions.</div><div><br /></div><div>With easee we decided that the UI is the IFU, and for purely regulatory reasons we needed to create a PDF document out of it to show to the regulators. But how? Doing this by hand would be error prone and a lot of work, as we have a new version every couple of weeks. Ideally we would automate the PDF generation for every release. Now this stupid regulatory requirement had turned into an interesting engineering challenge! I had the feeling that one of the puzzle pieces was hiding in the wizard engine, and also that this PDF generation would only be the beginning of something beautiful.</div><div><br /></div><div>The wizard engine can render pages based on just page parameters. If we made a "debug" renderer, we could show individual pages if we knew the parameters. Using selenium we could then create a screenshot of the page. But how do we capture all the pages in the eye exam?</div><div><br /></div><div>Well, the wizard engine can also capture all the page inputs and outputs in SessionStorage. We can store this "execution tree" scenario in a json file. So how this works is: you manually execute an eye exam, and at the last step press "store this scenario". Now we have captured all the page inputs. We store this json file next to the source code, and using a small debugging tool can jump to any step in the exam to debug things.</div><div><br /></div><div>In CI we can load these captured scenarios, and one by one render the pages in it and capture screenshots. If we create a small HTML page with all these screenshots in it, we should be good! We now have a simple IFU document of just screenshots. But we also would like some accompanying text. In Vue you have "single file components" that specify template, script and style tags. We added some other tags like "documentation". This is parsed in CI and added to the html page, and now we had auto generated screenshots per release! IFU done!</div><div><br /></div><div>Then I realized that we would also like to make really really sure that we don't unintentionally change anything in the UI of the eye exam. It would be neat if we can compare pages from before and after, and before a release check for "visual regression" problems.</div><div><br /></div><div>The diff viewer could do this too! During CI we were already storing the component descriptions and screenshots to AWS S3. We needed an app to quickly compare all screens in two versions of the software. Due to the combination of screens x screen sizes x page parameters, we had close to 5000 different screenshots. How to do this efficiently? I learned about image diffing and perception hashes. In the CI run I added a perception hash to the metadata files in S3.</div><div><br /></div><div>I created a quick & dirty Vue app to load two different versions of the exams and show the differences in screenshots. If there was a diff in the perception hash, it sends two images to a lambda function that shows the differences highlighted.</div><div><br /></div><div>As CTO you have to say that it was a "team effort", and that's true for almost all of the things that we do. I don't code much anymore. But while the team was working on the important roadmap stuff I got inspired. I built the entire CI screenshots scripts and the diff viewer in about two weeks by myself. I'm not really proud of that, I shouldn't be taking the fun projects for myself. Later on Rik and Mason started adding some features, and they took part of the ownership. Thanks for working on my hacky code guys.</div><div><br /></div><div>The easee diff viewer is a pretty cool multi-functional tool that compares thousands of images within seconds. It's awesome. We use it to verify that things looks good on multiple browsers and that we don't unintentionally change anything. I think it could be a separate SaaS product by itself. If only we had infinite time to launch new products.</div><h2 style="text-align: left;">2019 - easee RemoteControl</h2><div><div>The easee wizard engine was quite cool, and we used it to structure the code for our online eye exam. It executes whatever you see on your computer screen. It was not all good though. At some point in the exam your smartphone is paired using a websocket. The code for sending the phone what to display was a bit ugly. I had an inkling that there was a cleaner solution.</div><div><br /></div><div>We ended up with a special Vue component called "RemoteControl". This component has three props:</div><div>- remoteComponent</div><div>- remoteProps</div><div>- callback</div><div><br /></div><div>When you use the RemoteControl component, a couple of store methods are called. These will serialize the remoteComponent, remoteProps and the callback and send them over the websocket. At the other side the specified component is displayed with the props. A special prop for the callback function is created too, so the user input from the remote control can be sent back to the main computer.</div><div><br /></div><div>As the remoteProps and remoteComponent are reactive, the phone screen updates whenever the main computer decides to update the remote. It's magic.</div><div><br /></div><div>I can't share the code here as it's a bit entangled into our codebase, but the idea is so simple that you can probably create it within a day. There's a couple of caveats:</div><div>- the remote checks if it can find the remote component, if not, throw an error</div><div>- there should be only one RemoteControl active at the same time</div><div>- as network messages can be delayed, add an envelope with a random ID to see if the answer is actually sent to the current instance of the RemoteControl. If not, it might be from a previous version and you should discard it.</div><div><br /></div><div>As with the wizard engine, having the right abstraction layer can make the codebase a joy to work with. It certainly does in this case!</div></div><div><br /></div><h2 style="text-align: left;">2020 - Translation Mixer</h2><div>TODO</div><div><h2>2020 - easee TeleVisus</h2></div><div><div>Early 2020 was a strange time. I remember seeing some videos of sick people in China around January 1st, but my memory might be off. By late January it was clear we were in for trouble. In February it got closer and closer. One coworker next to me actually got covid, and I might have been infected too but did not have a lot of symptoms, I still don't know. At the end of February we went for a pubquiz with easee, and I told my coworkers "In two weeks time this won't be possible anymore". Indeed. Two weeks later to the day, all non-essential businesses had to close their doors. As a medical device company we technically were an essential business, but we decided not to play that card. Some other software companies in our sector did, just because they wanted butts in seats. Bastards.</div><div><br /></div><div>We had just had our first audit in November 2019 and now had a TUV Rheinland certified Quality Management System. This is a whole set of procedures that you have to follow when you do your day-to-day work, to guarantee safe and effective products. Setting it up had cost me blood, sweat and tears and my hair was much more gray than before. Our product certificate for our measuring function was not finished though. During Q1 2020 we had difficult calls with a tough German auditor. Francesco and I hated those as we were nice and non-confrontational. Robert loved it. Yves was somewhere in the middle but was extremely driven to get our certificate. I'd rather give up. I took a step back during those calls. Live and let live I suppose. Early March the auditors told us that our product was good to go, it just needed some paperwork on their side. It would take another 4 weeks. Great!</div><div><br /></div><div>On the evening of Thursday March 12th the government announced that offices could not open the next day. We closed the office and everyone had to work from home. A disorienting change for most of our employees. For me the spring of 2020 was great, I could spend more time with my kids and didn't have to commute for a change. Alas, there was no rest for the wicked. That same day, Yves had an idea to offer a limited test for hospitals as soon as possible. As care had to be offered remotely, hospitals needed to check patient's eye sight remotely too. This was a great opportunity for us and we all wanted to do our part. I asked for volunteers to work in the weekend. Everyone raised their hand. We jokingly said "What else are we going to do?" but actually we wanted to make a difference and do something meaningful. That weekend we made a plan and the team started running. We tried to do things according to our brand new QMS as much as possible, but our enthusiasm sometimes got in the way.</div><div><br /></div><div>On Tuesday we had the management team offsite to prepare for Q2 2020. This was Dennis (Head of Business Operations), Yves (CEO) and myself (CTO). We had already booked an Airbnb and if we interpreted the government rules a bit liberally we could have the meeting in person. I felt slightly guilty about this, but it was good as we were all a bit down. The stock market was crashing, travel was suspended, people were sick and dying. We could use the company. All of a sudden the mood changed. Yves now fully realized what a giant opportunity we had in front of us. The world had to adapt itself to remote work, remote care, remote everything! And what did we just get approved as a medical device for the entire EU? A <b>fully remote </b>online eye exam!</div><div><br /></div><div>This was the most chaotic management team offsite I've ever experienced. Yves' optimism always makes him a bit over-enthusiastic and hyperactive, but here he went up to 11. And rightly so, things were happening! We had one hour of useful meeting time in the twenty hours we had scheduled. The rest of the time Dennis and Yves were either calling our business partners, prospecting or preparing press releases. I was on Slack with my team constantly. Mason, Rik, Francesco and Danielle were going full-speed ahead with the new test, and I tried to bring some structure and answer a lot of questions. We had amazing people in the team and I was very proud of having hired them. Iris and some other people were updating the website left and right. We called the new test TeleVA for Tele Visual Acuity. Yves made an executive decision and renamed it to TeleVisus. I really liked that name, but we now call it the "easee online visual acuity test".</div><div><br /></div><div>We finished everything within two weeks, including usability testing, verification and validation. The TeleVisus test was put in our product in v1.6.118 and released on March 31st 2020. In any normal development cycle, it would have taken at least 6 months. This insane inefficiency is the sad reality in most companies, most of the time. This might be a good time to quote Colonel Kurtz from Apocalypse Now: "If they were committed, this war could be won with a fourth of our present force.". The big question for all managers is how to consistently get to this level of commitment at least some of the time. I don't have the answer. If you do, call me.</div><div><br /></div><div>Later that year we got in trouble for the TeleVisus release. We launched it without a measuring function claim, but when we finally got our certificate for that, we also applied it to TeleVisus. The auditors were not amused, as they had not reviewed this new part of the product. We should have explained this to them, but I was actually not aware that we had to as we did not yet have a clear "significant change" procedure. We got a couple of minor nonconformities for this, but in the end we were good. The measuring function was identical, and everyone understood we were trying to help hospitals during a huge crisis.</div><div><br /></div><div>TeleVisus is clearly my #1 project when it comes to teamwork. Technically it was not super challenging because we had all the components already. But the way we rallied together in a crisis situation.... it was simply incredible. A special memory full of light in those dark times.</div></div><h2>2020 - QMS Integrity Checker</h2><div><div>When we set out to get external certification for our product, we were in a bit of a bind. There's a whole bunch of international standards that have to be followed, and we had almost no experience with these. For Software Medical Devices there's at least ISO 13485, ISO 14971 and IEC 62304. The ISO 13485 is interesting, as it's a set up of requirements for forming a "Quality Management System" (QMS).</div><div><br /></div><div>Why is it interesting? Well, there's many ways of achieving high quality products, and I believe strongly in "move fast and break things". That's a good approach in general and many companies are successful with it (Facebook, SpaceX). It takes guts though. The failures will happen, and you need people with a high risk-tolerance to accept them. One of the reasons SpaceX can pull it off, but NASA can't, is that the tax-payers in the US would probably not accept many failures, and Elon does. It kind of makes sense to say that pacemakers should not be built this way, but then again, you could say the same about rockets.</div><div><br /></div><div>It's safe to say that most medical devices don't take a "move fast and break things" approach. Rather the opposite. They try to prevent failure by over-engineering up-front as much as possible. For most people from the software world this is a bit of a shock. For me it certainly was. I had no idea what a QMS was, but we did have to build one.</div><div><br /></div><div>You can not read the rules for a QMS for free, you have to buy the document online for a hefty fee. When we found the standard for relatively cheap at evs.ee we could finally read how to set up a QMS. Or so we thought. It's ironic that the 13485 standard exists to make you explain your processes and records in writing, but the standard itself does not contain any explanations. It just says "do X, do Y, do Z". Never "Rule X exists because manufacturers often have unclear descriptions of responsibilities within the organization, so it's unclear who can sign off on certain decisions". No, it just says "The organization shall document roles and responsibilities." It's a bit like reading a compiled program, stripped of all comments or useful variable names. This gives you a lot of freedom for interpretation, but no clear path forward.</div><div><br /></div><div>Once we had more or less deciphered what we had to do, we figured out that a QMS is a bit like a computer program. There are users, roles, processes with inputs and outputs. It's super systematic. With our limited understanding we set out to build a computer program to contain our procedures, technical documentation and records. It would publish everything as a static website with a git-based database. This was fun and pretty cool to build, but we kind of got lost in the engineering details instead of the QMS details. We also realized that we'd need engineers to operate every part of our QMS because it was using git. That was not so good, because it excluded a large part of our company from contributing. We could also not download all html pages and submit them as PDFs to an auditor. This was very bad. A couple of weeks before we had to submit our QMS for external certification, we reverted back to the industry standard approach. That is A4 documents in Google Docs, then printed out and put signatures on them. Terrible, but it got the job done. We got certified with this system in 2019. We abandoned the QMS static page program we had built and loved.</div><div><br /></div><div>The systematic approach was still there though, hidden within our Google Docs and Sheets. In the little spare time that I had I wanted to see if I could use Google Drive, Docs, and Sheets APIs to make a sort of "integrity checker" for our QMS. I had a lot of fun to talk to the APIs and parse the document structure in a Google Doc. At the end I even had a Google Docs to Markdown converter. What's more, I was able to parse all the roles, responsibilities, cross-links to other documents and much more. E.g. if you refer to a "Product Owner" in a procedure, but you haven't defined that role within your organization, it will throw an error. It scans hundreds of documents in a couple of minutes. Although it's still in its infancy, I run the integrity checker every now and then and point my colleagues to tiny mistakes in their documents. That's pretty fun and I look really smart. In theory this program combines best of both worlds: your regulatory people deal with Google Docs but the QMS can still be completely checked automatically using software. Might be a cool business idea actually. The plan was to incorporate this in some kind of CI/CD or monitoring system, but I never prioritize it and it just runs on my laptop. It's written in Python (what else) and the code is ugly but I love it.</div><div><br /></div><div>I personally struggled a lot while getting certified, it was probably the most stressful period of my life. Afterwards I found out that we are not alone, many other software companies are struggling with the same things, and we could really help each other out. I got in touch with Oliver (hi!) and he set up <a href="https://www.openregulatory.com" target="_blank">OpenRegulatory.com</a> with it's Slack group and I started a bi-monthly virtual <a href="https://www.meetup.com/european-samd/" target="_blank">SaMD meetup</a>. Both of these communities are thriving! Now, if you have any questions, just ask on Slack and experts will point you in the right direction right away. I dearly wish I had access to this in 2019. It's not a software solution, but I'm extremely proud of starting the community.</div><div><br /></div><div>Now that I'm a bit further in the regulatory field, I've also discovered that the rules do allow for a bit of "move fast and break things". The regulators realized that not everything can be checked beforehand, and rely on post-market surveillance to compensate. This catches problems that were not detected before. The feedback, CAPA and vigilance processes as described in ISO 13485 force you to learn from mistakes, and explain the serious problems to authorities. Realizing this made me feel very wise. Like a grown-up software rebel that realizes the entire world was thinking the same thing all along. Here's a quick shout-out to the people thinking about Chesterton right now.</div></div><h2>2021 - The WaterRower Camera</h2><div>I was getting more and more heavy during this period, almost reaching 90kg / 200lbs. I never exercised but I had bought a rowing machine with a water tank because I wanted to exercise, in principle. The machine was bought in 2019 just before the pandemic and during the boring early pandemic months it was a welcome way to distract myself. For some weeks I started rowing like a maniac but of course it wasn't sustainable. The machine started collecting dust.</div><div><br /></div><div>It had a small "workout computer" displaying the stats, but it was very dumb and not connected to anything. For some reason I thought it would be a good idea to measure my stroke speed, depth, and power more closely. The (black) elastic band is wound up around a cylinder. By painting the cylinder white and tracking with a camera how much black/white pixels are visible, it shows how far you've extracted the cord. Doing that a lot of times per second you can see stroke depth, speed, power, and recovery speed. I attempted to do this with a cheap ESP32 board with a camera but found that I could only get 1 FPS. I hooked up a raspberry pi zero 2 w and reached 60FPS. Then I built a simple web interface which I used to exercise about 5 extra times.<br /><br /></div><div>Then my wife's cousin Johannes came by, saw me rowing and laughed his a** off. Apparently my rhythm was completely wrong and I should do short and very fast pulls, then a slow recovery. I had taught myself rowing in the wrong way and now couldn't get used to the right away anymore. I haven't rowed since, but I discovered that running is very nice. In the summer of 2022 I lost 13 kgs and got down to 77kg and feel a lot healthier.</div><div><h2 style="text-align: left;">2021 - FDA Crawler</h2><div>Some industries see a lot of competition even though there's not a lot of commercial traction yet. There the battles are fought not over customers and revenue, but over patents and certifications. With easee we were operating in a similar environment. It was very important for me as CTO / product person to figure out what our competitors were doing. One of the tools that I developed for this is the FDA crawler. It scrapes the FDA API for product and company registrations and serializes it in a git repository. Every week the script runs and with the git history you can see exactly if there are any created, updated or deleted listings. Any new company or product category is instantly visible. Well, sort of, because the database is only updated manually once every couple of weeks. We typically see news via a google alert weeks before it is visible in the official channels. Nevertheless, this serialization to git is a nice pattern that I hope to use for more purposes. It helps us keep track of any new competitors in the USA. It's a tiny script but really effective and sure beats checking the database manually.</div></div><h2 style="text-align: left;">2022 - Cowboy e-Bike Remote Control</h2></div><div><div>In early 2021 I was a bit down. It was winter, some team members had just quit and I needed to distract myself. Due to covid lockdowns and boredom I gained more weight and drank more. I needed exercise (yes I had a water rower but I was too lazy to use it. I had to cycle every day so a bicycle would be a good idea).</div><div><br /></div><div>I started looking at e-bikes, because having experienced the rentals on a trip to Berlin I was sold. It makes cycling even more fun, and you end up taking the bike for most of your trips.</div><div><br /></div><div>The two options were the VanMoof S3 and the Cowboy V3. The Cowboy won. Less quality issues, sleeker look & minimalistic approach. I love this bike. It brings a smile to my face whenever I step on it.</div><div><br /></div><div>Sometimes the smile turned upside down because my phone would not connect to bluetooth though. Stephanie also uses the bike and her phone would be connected instead, or something else went wrong. It was just not super reliable. If things that are almost magical don't work reliably, it's very annoying for some reason. You know how great it can be, so why can't it be that great all the time? If you already read about InstaDeploy you can see that I'm guilty of this as well in my own creations.</div><div><br /></div><div>Anyway, I learned about the "hacking" apps for the Cowboy bikes, and because some were open source, I saw they used bluetooth. I then started a 1 year quest to create a bluetooth-powered car key for my e-bike.</div><div><br /></div><div>I had already dabbled a bit with ESP8266 and ESP32, but only using some simple wifi apps using Arduino IDE. Here I was out of my comfort zone.</div><div><br /></div><div>It's a small-sized hardware solution, no prototyping boards allowed. I had never done that before. The key should work from a coin cell for 1+ year and optimized for ultra low power. Not done before. It also needed to work with Bluetooth, BLE, whatever. No experience whatsoever. Fun, let's go!</div><div><br /></div><div>I quickly saw the the ESP32 would not work, it used too much power for a coin cell and was quite bulky. So, the nRF52 series might be an option.</div><div><br /></div><div>I had to learn a lot. Ordered a nRF52-DK (Development Kit) and started creating some code for connecting to BLE devices. As the Nordics team is transitioning between two versions of their SDK, I was very confused most of the time. The documentation seemed to apply and then not apply to my situation at random.</div><div><br /></div><div>Anyway, I persevered. I did it in evenings and weekends, and I believe it took me about 2 or 3 full time weeks of work. You can view the end result here: <a href="https://blog.waleson.com/2022/03/the-worlds-first-cowboy-key-fob.html">https://blog.waleson.com/2022/03/the-worlds-first-cowboy-key-fob.html</a></div><div><br /></div><div>I'm still working on getting the version with the PCB to work. For some reason the power cuts out after an hour or two, even though there should be no current. Still debugging that one.</div><div><br /></div><div>I'm currently using my prototype. It's beautiful on the outside but ugly inside. Every day I walk to my bike, press the button on my key and unlock it. Love it.</div></div><div data-block="true" data-editor="2762k" data-offset-key="9vc9k-0-0" style="background-color: #fafafa; border: 0px; color: #111111; font-family: "Libre Baskerville", serif; font-size: 17px; letter-spacing: -0.2px; margin: 0px; outline: 0px; padding: 0px; white-space: pre-wrap;"></div>Jouke Walesonhttp://www.blogger.com/profile/12215646682936082041noreply@blogger.com4tag:blogger.com,1999:blog-4710497652927856789.post-67100768400793093892022-04-03T13:15:00.001+02:002022-04-03T13:15:30.932+02:00ESP EYE pinout of GPIOS connected to DOUT, DIN, CLK, CS leads<p>I have an ESP-EYE v2.1 module and wanted to connect some more peripherals using GPIO. However, the board doesn't have any GPIO pins available. There are some soldering taps available, labeled DOUT, DIN, CLK and CS, but there is no documentation about these pins.</p><p>By tracing the leads and referencing the esp32 datasheet I managed to find the following scheme:</p><p>DOUT -> MTDI -> GPIO 12 (GPIO12)</p><p>DIN -> GPIO 2 (GPIO2)</p><p>CLK -> GPIO 0 (GPIO0)</p><p>CS -> GPIO 19 (GPIO19)</p><p>Ground is connected to the bottom of the side button, so I now soldered two wires and am using an external LED flash to make pictures using CS and the GND connections.</p><p>I hope this helps you!</p><p><br /></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjJGNQl2XP9tXnWE9PjiNRuwHo6Rgj0V4y5dKPzOE76ZxRt3SUrf4IxWsdGHKiNmds-9wcJkvcfWhQiThuYhDt3cAbxG_AdAjiYBB28AfsgPINETLzdiHAwPEYLZ3J8Bo9S3D_fExMz2NidJIenQ0kk5XHNHNj06DdZWtpYdONKLfog0YcaiLC2ugQh/s3024/IMG_0394.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="3024" data-original-width="2268" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjJGNQl2XP9tXnWE9PjiNRuwHo6Rgj0V4y5dKPzOE76ZxRt3SUrf4IxWsdGHKiNmds-9wcJkvcfWhQiThuYhDt3cAbxG_AdAjiYBB28AfsgPINETLzdiHAwPEYLZ3J8Bo9S3D_fExMz2NidJIenQ0kk5XHNHNj06DdZWtpYdONKLfog0YcaiLC2ugQh/s320/IMG_0394.JPG" width="240" /></a></div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh8uGb3ckpXBRP8QOwwRxsKni5glXs3EQVh-uhwATL-pklOT_ZDxQ-OFctKTcUEaN7sNapjmNorCAr1rxWFgW-EEQaTVkHxOdUD3FV4DGhr0moirVJwe-xYYI0X8HGVMCHOsmD4vldNUdLrxzFL3E6FPWL0SZN1lFje5jHHLo3XE-nYfDlssIjGSe8C/s3024/IMG_0395.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="3024" data-original-width="2268" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh8uGb3ckpXBRP8QOwwRxsKni5glXs3EQVh-uhwATL-pklOT_ZDxQ-OFctKTcUEaN7sNapjmNorCAr1rxWFgW-EEQaTVkHxOdUD3FV4DGhr0moirVJwe-xYYI0X8HGVMCHOsmD4vldNUdLrxzFL3E6FPWL0SZN1lFje5jHHLo3XE-nYfDlssIjGSe8C/s320/IMG_0395.JPG" width="240" /></a></div><br /><p><br /></p>Jouke Walesonhttp://www.blogger.com/profile/12215646682936082041noreply@blogger.com0tag:blogger.com,1999:blog-4710497652927856789.post-77152650213738587382022-03-29T23:35:00.007+02:002023-07-14T11:28:39.973+02:00The world's first Cowboy key-fob<p>Last year I got my <a href="https://cowboy.com/" target="_blank">Cowboy</a> 3 e-bike and I love it. Every day I start riding the bike it feels great, it makes cycling even better than it already is. My only gripe is unlocking the bike with the app, which takes about 15 seconds. This post is about how I built a BLE powered 3 button key that unlocks my bike, and the physical key unlocks the battery.</p><p>Here it is in action:</p><br /><div class="separator" style="clear: both; text-align: center;"><iframe allowfullscreen='allowfullscreen' webkitallowfullscreen='webkitallowfullscreen' mozallowfullscreen='mozallowfullscreen' width='320' height='266' src='https://www.blogger.com/video.g?token=AD6v5dxPaly-YzVjM_zQUqwkIYVB2CglBPTw6Ly3IvTJH6O46--iVBoDbRlY6YGF_764FGLjchV15NTAnf-apwgnCA' class='b-hbp-video b-uploaded' frameborder='0'></iframe></div><br /><div style="text-align: left;">Is it faster? Maybe. But cool? Definitely.</div><h3 style="text-align: left;">But why?</h3><p>tldr: It doesn't make sense, but I learned a lot and it was fun.</p><p>I used the official Cowboy app with a cheap Android phone (Moto G7 Play) and also an my wife's iPhone SE (both first and second gen). On the iPhones the Bluetooth works a lot better, once the app opens it's almost instant. But: unlocking takes about 15 seconds: taking your phone out of your pocket while you're fiddling with the lock (7s), finding the app (3s), opening it (3s), unlocking (2s). If you do this 2 times a day for 365 days it will take 3 hours. So, I decided to spend a week and a couple hundred euros building a solution for this... :D</p><h3 style="text-align: left;">Goal</h3><p>I wanted the experience to be more like my car key. So, I set out to build a foldable key fob with 3 buttons: turn on, turn off, toggle the lights. The physical key should unlock the battery.</p><p>Here is the result:</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhRsWFYb-YYYJDCel--8T6DAAL0jK_RcahsVW1YB_W-4Lj7goA0H0NXru7vyjcMxiFm2hqOI88GgUAh3Ug3Lb5SMGYx2Xxi5Okt-uSJ-Tl0QUdwGzx12B5utGaPwtAG2FwV2pOQok4yAmbR6RxDNfzllg3Nn_qgXwlEwbSjw4JJk2jUrnGPdp_i7KnR/s4032/IMG_0387.JPG" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="3024" data-original-width="4032" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhRsWFYb-YYYJDCel--8T6DAAL0jK_RcahsVW1YB_W-4Lj7goA0H0NXru7vyjcMxiFm2hqOI88GgUAh3Ug3Lb5SMGYx2Xxi5Okt-uSJ-Tl0QUdwGzx12B5utGaPwtAG2FwV2pOQok4yAmbR6RxDNfzllg3Nn_qgXwlEwbSjw4JJk2jUrnGPdp_i7KnR/s320/IMG_0387.JPG" width="320" /></a></div><p></p><h3 style="text-align: left;">Starting with ESP32</h3><p>First I built a prototype with an ESP32 dev board using the Arduino IDE. Got everything working, but then figured out that even the separate chip would never fit in a car key enclosing and could not be powered by a CR 2032 coin cell, as the ESP requires quite a lot of power. I have to say that the ESP32 was a joy to program. Learning about BLE GATT took some time, but then copying the commands and UUIDS from the Bronco Unleashed source code was super easy. But anyway, in the end it didn't work because of the power constraints.</p><h3 style="text-align: left;">Going for nRF52</h3><p>After doing some more research I found the nRF52 ecosystem. The chips are tiny and very low power. It should easily be able to last a year on a CR2032. However, the programming experience was much much worse than the Arduino IDE. There is a big migration from the nRF5 SDK to the nRF Connect SDK, and most posts don't mention this. Also, the IDE targets SVN instead of GIT., it is all very niche. Nevertheless, after a couple of weekends I got it to work.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj1FuUfZnlleWdgci2MDLhWqdb_xDHIGIZqMQrqEflV4s-73nT3RAGFBGpnjxTgt8qOU4-BybVXOj5i9mHEthK8JlUQdtHw64s0N_wujApOazTxphpnjkVovwqAp6YA3dEjXchwSkt1PlCdtIrMYgP8FSoJOeoO_VxKjL9SKCgoyA4oMsXY9KonbT_D/s4032/IMG_0383.JPG" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="3024" data-original-width="4032" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj1FuUfZnlleWdgci2MDLhWqdb_xDHIGIZqMQrqEflV4s-73nT3RAGFBGpnjxTgt8qOU4-BybVXOj5i9mHEthK8JlUQdtHw64s0N_wujApOazTxphpnjkVovwqAp6YA3dEjXchwSkt1PlCdtIrMYgP8FSoJOeoO_VxKjL9SKCgoyA4oMsXY9KonbT_D/s320/IMG_0383.JPG" width="320" /></a></div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi8YcWXKAF4SkFO6GGXZdgnpkCsIh9-GYmQA69nIwMK2iUvlP6h_Hyg0SygEyF_1MeYugB508wTHh25To4tc8VgwxhgTpz1xkrO3NsVAGOYlXNOX4XgaYwiLw3snsTkuzUDOTUkIM7SziL2HzGUvyry9lJGWyY4anTL9jJr2hbzccdhIWI1n9HFhThU/s3024/IMG_0386.JPG" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="2268" data-original-width="3024" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi8YcWXKAF4SkFO6GGXZdgnpkCsIh9-GYmQA69nIwMK2iUvlP6h_Hyg0SygEyF_1MeYugB508wTHh25To4tc8VgwxhgTpz1xkrO3NsVAGOYlXNOX4XgaYwiLw3snsTkuzUDOTUkIM7SziL2HzGUvyry9lJGWyY4anTL9jJr2hbzccdhIWI1n9HFhThU/s320/IMG_0386.JPG" width="320" /></a></div><div><br /></div>*note -> this was a prototype, the version I'm using now uses a proper PCB. Look at the <a href="https://github.com/jtwaleson/cowboy-bike-remote-ble" target="_blank">github repo</a> for more details.<br /><h3 style="text-align: left;">Ingredients:</h3><p></p><ul style="text-align: left;"><li>Fanstel <a href="https://www.fanstel.com/bc805" target="_blank">BC805M</a> or BT832A, I tried both, the cheaper one is good enough.</li><li>3 buttons</li><li>A led + resistor</li><li>A CR2032 or CR2025 3v coin cell battery</li><li>Diodes for routing power only when the buttons are pressed</li><li>Car key fob shell, look on eBay</li><li><a href="https://github.com/jtwaleson/cowboy-bike-remote-ble" target="_blank">Source code</a> (also contains the ESP32 code)</li></ul><p></p><h3 style="text-align: left;">Needed tools:</h3><div><ul style="text-align: left;"><li>nNR52-DK for debugging and flashing the chip</li><li>Glue gun</li><li>Dremel for cutting out the original key and for making room in the new key fob shell</li></ul><h3 style="text-align: left;">Learned:</h3></div><div><ul style="text-align: left;"><li>The nRF toolkit plus horrible cMake configuration options...</li><li>Sniffing BLE network traffic</li><li>Brushed up my skills in C, soldering micro connections, electronics</li><li><strike>That the bike sends a trip summary even without the app! The full route details are not there, but the distance and destination is recorded.</strike></li><ul><li>Update: untrue, I did not unpair my phone so in the background it would sometimes connect, get the latest trip data and synchronize with the Cowboy servers. After unpairing my phone completely, the trips did not show up in the app anymore.</li></ul><li>Battery usage profiling is difficult. The battery would drain in a couple of days. I solved this by using diodes to only power the chip while a button is pressed. As this takes +/- 2 seconds it also prevents against some accidental presses.</li></ul></div><h3 style="text-align: left;">Next steps..</h3><div><ul style="text-align: left;"><li>For some weird reason I can't switch the lights on and off. See <a href="https://devzone.nordicsemi.com/f/nordic-q-a/86366/nrf52-dk-in-nrf52805-mode-sending-long-ble-uart-command-works-on-dk-not-on-external-chip" target="_blank">this thread</a> </li><ul><li>Solved! I figured out that you need to be subscribed to the notify of the UART RX characteristic. Updated in the (horrible!) source code.</li></ul><li>Now that the light is solved, I'd like to toggle it. The next question is how to decode the Dashboard service data. This is variable length and will require some reverse engineering or insider knowledge ;)</li><ul><li>Solved too! You can read out the memory.</li></ul><li>It's very important for me to switch between speed limits 24km/h and 25km/h by pressing a combination of buttons.</li><ul><li>Solved too.</li></ul><li>Obviously this project is not complete without some kind of "beep beep" sound when unlocking. I don't think the Cowboy has a speaker, but this has to be fixed somehow.</li><li>Update: The battery is not replaceable as I could not fit this into the existing enclosure. I went on and designed my very first custom PCB that will allow for user replaceable batteries. Currently waiting for the components and will do a follow-up post once everything works.</li></ul></div><div><br /></div>Jouke Walesonhttp://www.blogger.com/profile/12215646682936082041noreply@blogger.com0tag:blogger.com,1999:blog-4710497652927856789.post-66117597391689581992021-09-17T13:34:00.001+02:002021-09-17T13:34:03.727+02:00Thinkpad L460 webcam issues on Windows 10<p> Some colleagues reported flickering webcam issues on their L460 computers. The camera turns on for a couple of seconds and then stops working. With all this remote work it's really annoying.</p><p>The fix is really simple, just rename the RSProviders folder in C:\Program Files\Realtek. See here for more details: https://www.winhelponline.com/blog/webcam-anniversary-update-windows-10-yuys-standard/</p><p>I suspect this applies to L450 and L470 models too.</p>Jouke Walesonhttp://www.blogger.com/profile/12215646682936082041noreply@blogger.com0tag:blogger.com,1999:blog-4710497652927856789.post-76033335380527943602021-08-23T21:18:00.003+02:002021-08-23T21:19:55.131+02:00Lego Mindstorms NXT Brick on Windows 10I have an old NXT Brick at home, and tried to get it to run to teach my son a bit of programming. It turns out that this is quite hard in 2021 with Windows 10.<div><br /></div><div>After a while I figured it out! It turns out that there are 3 types of software:</div><div><ol style="text-align: left;"><li>The old NXT Software -> does not work in Windows 10 </li><li>The new EV3 app (from the Microsoft store) -> does not support NXT Brick </li><li>The Retired EV3 app (installable .exe) -> Supports Windows 10 and has a backwards compatible mode for NXT Bricks. </li></ol></div><div><br /></div><div> So, if you have an NXT Brick and Windows 10, you need the Retired EV3 software. It's shown on <a href="https://www.lego.com/en-nl/themes/mindstorms/downloads">the lego website</a> like this:</div><div><div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgnYl_8vDH9tLFO2p5IHUXhHr5UsG8TmGTsthcqKgM9CC-FXYM1KaoGX5Y6sRj6lX3SJQ_deSh8AKl0uZIO3DNEVqZCvL4kMMm7HAXbwSvjetghbc-YeOj0WURuLE3A3m2Mk7Mv-EKZS0E/s936/Screenshot+from+2021-08-23+21-16-08.png" style="display: block; padding: 1em 0px; text-align: center;"><img alt="" border="0" data-original-height="617" data-original-width="936" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgnYl_8vDH9tLFO2p5IHUXhHr5UsG8TmGTsthcqKgM9CC-FXYM1KaoGX5Y6sRj6lX3SJQ_deSh8AKl0uZIO3DNEVqZCvL4kMMm7HAXbwSvjetghbc-YeOj0WURuLE3A3m2Mk7Mv-EKZS0E/s320/Screenshot+from+2021-08-23+21-16-08.png" width="320" /></a></div>
I hope this helped you.
</div>Jouke Walesonhttp://www.blogger.com/profile/12215646682936082041noreply@blogger.com0tag:blogger.com,1999:blog-4710497652927856789.post-74240827436364570722021-07-10T13:13:00.022+02:002021-07-10T14:27:37.006+02:00AI Text-to-Speech models have accents too<script>
speechSynthesis.onvoiceschanged = function () {
speechSynthesis.getVoices().forEach(function(voice) {
var x = document.getElementById("voices");
var option = document.createElement("option");
option.text = voice.lang + " - " + voice.name;
option.value = voice.voiceURI;
if (voice.lang.indexOf('nl') === 0) {
option.selected = true;
}
x.add(option);
});
}
speechSynthesis.getVoices();
function speak(event) {
event.preventDefault();
var x = document.getElementById("voices");
var y = document.getElementById("text");
let utterance = new SpeechSynthesisUtterance(y.value);
speechSynthesis.getVoices().forEach(function(v) {
if (v.voiceURI === x.value) {
utterance.voice = v;
utterance.lang = v.lang.replace("_", "-");
}
});
speechSynthesis.speak(utterance);
return false;
}
function speaktext(event, langCode, text) {
event.preventDefault();
let utterance = new SpeechSynthesisUtterance(text);
utterance.lang = "en-US";
let done = false;
speechSynthesis.getVoices().forEach(function(v) {
if (!done && v.lang.indexOf(langCode) === 0) {
utterance.voice = v;
utterance.lang = v.lang.replace("_", "-");
done = true;
}
});
speechSynthesis.speak(utterance);
return false;
}
</script>
<p>While playing around with the <a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_Speech_API" target="_blank">Web Speech API</a> I discovered something interesting: if you let the Dutch voice speak English text, it sounds remarkably like the typical "Dungrish" accent (Dutch person speaking English).</p><p>I didn't expect this. Obviously the AI was not trained on reproducing foreign accents, but it still does!</p><p>Very interesting to see what happens if you let the voice speak a language it was not trained on</p><p>You can play around with it here if your browser supports it, or try the examples below:</p>
<div>
<form onsubmit="return speak(event)">
<label for="voices">Select the voice, this might be empty if your browser doesn't support text to speech</label>
<select id="voices" name="voices"></select>
<br/>
<label for="text">Enter the text you want spoken</label>
<textarea id="text" name="text" cols="100" rows="10">Hello good sir, can you please tell me what twenty times ten is?</textarea>
<br/>
<input type="submit" value="Speak">
</form>
</div>
<p>Or try these ready made examples:</p>
<ul>
<li><a onclick="return speaktext(event, 'nl', 'How do you do good sir? This is a Dutch accent.')" href="#">English text spoken by Dutch voice</a></li>
<li><a onclick="return speaktext(event, 'hi', 'How do you do good sir? This is a Hindi accent.')" href="#">English text spoken by Hindi voice</a></li>
<li><a onclick="return speaktext(event, 'it', 'How do you do good sir? This is an Italian accent.')" href="#">English text spoken by Italian voice</a></li>
<li><a onclick="return speaktext(event, 'fr', 'How do you do good sir? This is a French accent.')" href="#">English text spoken by French voice</a></li>
<li><a onclick="return speaktext(event, 'de', 'How do you do good sir? This is a German accent.')" href="#">English text spoken by German voice</a></li>
<li><a onclick="return speaktext(event, 'ja', 'How do you do good sir? This is a Japanese accent.')" href="#">English text spoken by Japanese voice</a></li>
<li><a onclick="return speaktext(event, 'en', 'Hoe gaat het met u? Dit is een Engels accent.')" href="#">Dutch text spoken by English voice</a></li>
<li><a onclick="return speaktext(event, 'de', 'Hoe gaat het met u? Dit is een Engels accent.')" href="#">Dutch text spoken by German voice</a></li>
<li><a onclick="return speaktext(event, 'nl', 'Wie geht es dir? Das ist ein niederländischer Akzent.')" href="#">German text spoken by Dutch voice</a></li>
<li><a onclick="return speaktext(event, 'en', 'Wie geht es dir? Dies ist ein englischer Akzent.')" href="#">German text spoken by English voice</a></li>
</ul>
<p>Once you're done playing around, <a href="https://news.ycombinator.com/item?id=27792773" target="_blank">discuss this on HN</a></p>
<p><br /></p><p><br /></p>Jouke Walesonhttp://www.blogger.com/profile/12215646682936082041noreply@blogger.com0tag:blogger.com,1999:blog-4710497652927856789.post-15128744441263949272020-10-21T18:29:00.004+02:002020-10-22T09:10:32.581+02:00Default HTML5 camera input broken on Android / Chrome<p>I'm trying to create a simple web app for smartphones that lets the camera app take a picture.</p><p>The standard html5 is super simple:</p><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><p><span style="font-family: courier;"><input type="file"</span></p><p><span style="font-family: courier;"> accept="image/png, image/jpeg"></span></p></blockquote><p><a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file">https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file</a></p><p>So I tried this.</p><p><br /></p><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><p style="text-align: left;"><span style="font-family: courier;">$ cat index.html</span></p><p style="text-align: left;"><span style="font-family: courier;"><!doctype html></span></p><p style="text-align: left;"><span style="font-family: courier;"><html></span></p><p style="text-align: left;"><span style="font-family: courier;"> <body></span></p><p style="text-align: left;"><span style="font-family: courier;"> <form action="/store-image/"></span></p><p style="text-align: left;"><span style="font-family: courier;"> <label for="img">Select image:</label></span></p><p style="text-align: left;"><span style="font-family: courier;"> <input type="file" id="img" name="img" accept="image/*" capture="camera"></span></p><p style="text-align: left;"><span style="font-family: courier;"> <input type="submit"></span></p><p style="text-align: left;"><span style="font-family: courier;"> </form></span></p><p style="text-align: left;"><span style="font-family: courier;"> </body></span></p><p style="text-align: left;"><span style="font-family: courier;"></html></span></p></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px;"><p style="text-align: left;"><span style="font-family: courier;">$ python3 -m http.server </span> </p></blockquote><p><br /></p><p>I go to my phone.</p><p><br /></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh043yRKFg4aJzUJsXwvU1Mt9BH1WojlH84o6U6sMCjWYbsvM4bvdbMuWSGnXigSicgF9lu8T6iBEnd9vKgDlXRFZUVwQLuHG61eiMoMNVNv3VKjXziT69POvMiIHufLNQVpmXZqyEb6XA/s1512/Screenshot_20201021-175638.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1512" data-original-width="720" height="640" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh043yRKFg4aJzUJsXwvU1Mt9BH1WojlH84o6U6sMCjWYbsvM4bvdbMuWSGnXigSicgF9lu8T6iBEnd9vKgDlXRFZUVwQLuHG61eiMoMNVNv3VKjXziT69POvMiIHufLNQVpmXZqyEb6XA/w305-h640/Screenshot_20201021-175638.png" width="305" /></a></div><br /><p>Camera pops up, I submit the picture, and ...</p><p><br /></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi3oKi3yrDJSWeie5fuwB8MQMqPrWgWNvx7N3Mvb0LGxaJLTRqepReFKCiqkweqBvBX6CFSsK9DhMCajTZ3qwQXE8Og-e6SyAPl9IwLC-AhCIrVCEoxr1sXpueFp7Uqadnr09PR6DqV3Yk/s1512/Screenshot_20201021-175656.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1512" data-original-width="720" height="640" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi3oKi3yrDJSWeie5fuwB8MQMqPrWgWNvx7N3Mvb0LGxaJLTRqepReFKCiqkweqBvBX6CFSsK9DhMCajTZ3qwQXE8Og-e6SyAPl9IwLC-AhCIrVCEoxr1sXpueFp7Uqadnr09PR6DqV3Yk/w304-h640/Screenshot_20201021-175656.png" width="304" /></a></div><br /><p>"Unable to complete previous operation due to low memory".</p><p>Ok, the simplest stock html5 sample doesn't work. Weird. I have a simple Moto G7 Play running Android 10. A 1.5 year old cheap Android phone, just like 80% of the world uses. My memory usage is really not special.</p><p>What do Google and Stackoverflow say?</p><p><br /></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEggfw6xgChi8bnvFPDvn-E-XCu0L8GBr2KbSCmEnkR6HdtjZ_0JpCP6FkNgXGodfGnuxnUH92q-VivU-0f29Kmp5fxPD8OF7Ckjhi9KA5X_i1VgYHmiRIwpJK1Pqn8eY6-BlFArNScFsFI/s1102/Screenshot+from+2020-10-21+18-15-17.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="151" data-original-width="1102" height="88" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEggfw6xgChi8bnvFPDvn-E-XCu0L8GBr2KbSCmEnkR6HdtjZ_0JpCP6FkNgXGodfGnuxnUH92q-VivU-0f29Kmp5fxPD8OF7Ckjhi9KA5X_i1VgYHmiRIwpJK1Pqn8eY6-BlFArNScFsFI/w640-h88/Screenshot+from+2020-10-21+18-15-17.png" width="640" /></a></div><p><a href="https://c.realme.com/in/post-details/1317318985611476992">https://c.realme.com/in/post-details/1317318985611476992</a></p><p>Great, I'll tell my users to go to "Free up the ram". Even if I would do that, it doesn't work.</p><p>Or this one from 2017.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjUy2_wpSvWMc6P7y40fF4I1r76SMK2kjmEg96Uafw4E9RjRt9U3uWgTbWIQQxNbf9YKS7G1TC4Dzye9DBtMbYINBM-9JOdDrlZf6m1I051HCkVVZ1aNEd8jY5H3jXSv60odTvTFPX-t1E/s720/Screenshot+from+2020-10-21+18-18-29.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="634" data-original-width="720" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjUy2_wpSvWMc6P7y40fF4I1r76SMK2kjmEg96Uafw4E9RjRt9U3uWgTbWIQQxNbf9YKS7G1TC4Dzye9DBtMbYINBM-9JOdDrlZf6m1I051HCkVVZ1aNEd8jY5H3jXSv60odTvTFPX-t1E/s320/Screenshot+from+2020-10-21+18-18-29.png" width="320" /></a></div><br /><p><a href="https://mavislibrary.blogspot.com/2017/07/solution-to-unable-to-complete-previous-operation-due-to-low-memory-solution.html">https://mavislibrary.blogspot.com/2017/07/solution-to-unable-to-complete-previous-operation-due-to-low-memory-solution.html</a></p><p>Sure, I'll tell my users to switch to another browser! Or to enable Developer Options and select an arcane option in the hidden menu! I have to say that both of these do actually work so they're better than the previous solution.</p><p>So what the hell? The simplest html5 example doesn't work for 80% of the Android users? Honestly I'm pulling the number out of thin air, but I think it's reasonable to assume that 80% of the world has a crappier phone than myself. If you Google some more, you'll see that all kinds of people have come up with the "Developer Option" workaround. There is no real solution. My guess is that everyone has given up and switched to camera capture from within the browser, or maybe that most developers are using phones that don't have this problem.</p>Jouke Walesonhttp://www.blogger.com/profile/12215646682936082041noreply@blogger.com0tag:blogger.com,1999:blog-4710497652927856789.post-74207275368791860422019-06-23T20:51:00.000+02:002019-06-23T20:51:13.762+02:00Comments as first class citizensIn the future it will be possible to link comments to sections of code, instead of just writing them a couple of lines above of it. Today, comments are second-class citizens that frequently get out of sync with the reality of the code. This is because working on code is done when the brain is in spotlight mode: we first look at this piece of code, which calls this function, which calls this method, etc. . The brain jumps from line to line, and misses the context around it. Documentation and comments are more holistic and should not be in-line with code, as they are something different entirely. Comments are "about" the code, not code itself.<br />
<br />
In a merge request, you should see the changed piece of code with the comments that are applicable to it. These can be locally (like the comments that today are in the same file), but also an architecture document could refer to sections of code that implement the architecture. In the IDE, and certainly in a merge request, the developers should see if their changes still match the documented functionality and architecture.<br />
<br />
Tools like <a href="https://code.visualstudio.com/blogs/2017/02/12/code-lens-roundup">Code Lens </a>slowly get us into the right direction.Jouke Walesonhttp://www.blogger.com/profile/12215646682936082041noreply@blogger.comtag:blogger.com,1999:blog-4710497652927856789.post-47088152543824692972019-06-11T09:50:00.004+02:002019-06-11T09:53:44.637+02:00What's so terrible about software?[this is a work in progress]<br />
<br />
One of the things you should understand before reading this series of blog posts, is why I think software is so terrible. <a href="https://blog.waleson.com/2019/06/whats-so-great-about-software.html">I also think it's amazing, but that's another post.</a><br />
<br />
<h3>
1. Very little standardization</h3>
Check out this <a href="https://landscape.cncf.io/" target="_blank">little map </a>from the Cloud Native Computing Foundation.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAB0OAzJvOPTq0tM1m0OZ-KB2m05R2bmLAOxP_e5UwgO4e_PLHc9DVnXB4eDZDMmciYppJZLp-5lUGXmZGMK-vVSR9PLHIh3af6D5erpvqVTVIbC4_vvIQQ2f0erthMUoVsjUqMBfaD9A/s1600/Screenshot+from+2019-06-11+09-40-29.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="848" data-original-width="1377" height="246" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAB0OAzJvOPTq0tM1m0OZ-KB2m05R2bmLAOxP_e5UwgO4e_PLHc9DVnXB4eDZDMmciYppJZLp-5lUGXmZGMK-vVSR9PLHIh3af6D5erpvqVTVIbC4_vvIQQ2f0erthMUoVsjUqMBfaD9A/s400/Screenshot+from+2019-06-11+09-40-29.png" width="400" /></a></div>
<br />
This map is what's wrong with the software landscape today. My thoughts are very well expressed by the late Joe Armstrong <a href="https://www.youtube.com/watch?v=lKXe3HUG2l4" target="_blank">in this talk</a>.<br />
<br />
<h3>
2. Software "evolved" to where we are now</h3>
<br />
<br />
<h3>
3. Technology is disrupting our lives and our culture</h3>
<br />
You probably recognize these trends:<br />
<div class="separator" style="clear: both; text-align: center;">
<img alt="Image result for glued to phone" border="0" height="179" src="https://static1.squarespace.com/static/578a53c39f745617d626a2c4/57c7986c8419c27c5f2fd75a/590a0a4d1e5b6ce087691454/1494866975319/Kids+glued+to+phone.jpg?format=1500w" width="320" /></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<img alt="Image result for computer all day meme" border="0" height="227" src="https://pics.me.me/i-sit-at-workin-front-of-acomputer-all-day-just-5649271.png" width="320" /></div>
<br />Jouke Walesonhttp://www.blogger.com/profile/12215646682936082041noreply@blogger.comtag:blogger.com,1999:blog-4710497652927856789.post-83743152219805552942019-06-11T09:26:00.006+02:002019-06-11T09:52:22.675+02:00What's so great about software?[this is a work in progress]<br />
<br />
One of the things you should understand before reading this series of blog posts, is why I think software is so amazing. <a href="https://blog.waleson.com/2019/06/whats-so-terrible-about-software.html">I also think it's terrible, but that's another post.</a><br />
<br />
<h3>
1. It is ridiculously cheap to get started with software.</h3>
Carving wooden toys is probably cheaper because you only need wood and a knife, but for software, you need:<br />
- the ability to read and think somewhat logically<br />
- a second hand laptop, which you can buy for about $50 - $100<br />
- power<br />
- an internet connection (or a manual)<br />
<br />
Mind you, it will take lots of time and effort<br />
<h3>
2. It is practically free to copy digital resources.</h3>
<div>
All things that can be stored digitally can be copied for almost free. If you wanted to reach thousands of people before </div>
<h3>
3. It is ridiculously cheap to scale software.</h3>
<div>
WhatsApp famously scaled to a billion (?) users with a team of about 50 people. Their servers each connected millions of users at the same time. With clever engineering, anything is possible.</div>
<h3>
4. The amount of abstraction involved in programming is staggering, and there is plenty of room at the top.</h3>
<div>
Someone created a calculator in Minecraft. I estimate it's about a trillion times slower than your computer would normally calculate things, and still, the Minecraft calculator is faster than most humans! Computers are very, very fast.<br />
<br /></div>
<h3>
5. Since about 10 years, humans have gained the ability to connect to any person and any device at any time.</h3>
We have only started to scratch the surface of what we have made possible.<br />
<br />Jouke Walesonhttp://www.blogger.com/profile/12215646682936082041noreply@blogger.comtag:blogger.com,1999:blog-4710497652927856789.post-60570362037085457832019-05-11T11:53:00.000+02:002019-05-11T11:53:27.500+02:00The Future of ProgrammingSomething has been brewing in my mind for years about which I have unpublished blog posts, loosely linked ideas and frustrations in my daily life as a software developer. <i>I love software and I enjoy my job tremendously, but at the same time I believe that software creation is horribly inefficient and unnecessarily complex</i>.<br />
<br />
I've noticed that my thoughts are slowly becoming more consistent and I believe I can see where our industry is headed. Of course I can't predict how the future is going to play out, but A) I can dream about how software should be created and B) I can also see what it would take to get there. I'm going to write publicly about what I think could (or should) be <b>The Future of Programming </b>in a series of blog posts.<br />
<br />
This year is a good time to start thinking about this. We're at an interesting stage where we have thousands of languages, eco-systems,<br />
<br />
<b>Brewing </b>means taking materials, letting them ferment for some time and getting a very different product as a result. Only at the end you get to see if the brew is terrible, so-so, good or great. Well, here's my result and I hope you'll enjoy it.<br />
<br />
In this series, there will be a couple of types of posts:<br />
- Foundational: the central ideas that are the essence of everything I say or think about software<br />
- Highlighs: pointing out companies, products or experiments that are doing something very smart or interesting<br />
- Comments: my ideas on patterns, languages, habits<br />
<br />
<br />
<br />
Full disclosure: I worked at the Low-Code vendor Mendix for about 7 years. I have no financial ties to the company anymore. Many of my ideas came from that company and I strongly believe in its mission, which is to let Business and IT create software together. However, I think it's more important that the mission succeeds than that the company succeeds.<br />
<br />Jouke Walesonhttp://www.blogger.com/profile/12215646682936082041noreply@blogger.comtag:blogger.com,1999:blog-4710497652927856789.post-38383846522299532692019-02-11T10:31:00.002+01:002019-02-11T10:34:49.874+01:00UUIDs in MySQL follow upLast week my blog post on <a href="https://blog.waleson.com/2019/02/uuids-in-mysql-are-really-not-random.html" target="_blank">UUIDs in MySQL</a> stirred <a href="https://news.ycombinator.com/item?id=19085189" target="_blank">a discussion on Hacker News</a>.<br />
<br />
I wrote the post in about half an hour and stupidly enough, I did not think about UUID v1. I knew about v1 (from the Melissa virus case), v4 and v3 & v5 (which I once used to generate deterministic UUIDs). I just didn't link my case to UUID v1 because I've hard-wired UUID with UUIDv4 in my head.<br />
<br />
Here are two observations from the discussion on HN.<br />
<br />
<b>First</b>: over time, the first characters of UUIDv1 are still uniformly distributed.<br />
<br />
<br />
<br />
<script src="https://gist.github.com/jtwaleson/428bbf6f9507e90ff6f7508092748888.js"></script>
The only reason I had the same prefix was because I filled in all values with one UPDATE query when I initialized the column. If on the other hand you use <span style="font-family: "courier new" , "courier" , monospace;">UUID()</span> whenever you create a new row, the first characters will be distributed evenly.<br />
<br />
<b>Second</b>: the different UUID versions should be more clearly distinguished in general. The Zen of Python has a line:<br />
<blockquote class="tr_bq">
"Explicit is better than implicit."</blockquote>
In following this, I think it's better not to expose any <span style="font-family: "courier new" , "courier" , monospace;">UUID()</span> function, but only explicitly offer the typed versions <span style="font-family: "courier new" , "courier" , monospace;">UUIDv1()</span>, <span style="font-family: "courier new" , "courier" , monospace;">UUIDv4()</span> etc. This is already what most libraries do as far as I've seen.Jouke Walesonhttp://www.blogger.com/profile/12215646682936082041noreply@blogger.comtag:blogger.com,1999:blog-4710497652927856789.post-45132878387620266382019-02-05T14:36:00.003+01:002019-02-11T10:34:18.805+01:00UUIDs in MySQL are really not random<a href="https://en.wikipedia.org/wiki/Universally_unique_identifier" target="_blank">Universally Unique Identifiers</a> (UUIDs) are great. I love how you can tell the progress of a batch job just by looking at the current UUID. If it starts with <b>0...</b>, the task is less than 1/16th done. If it starts with <b>7d..</b>, we're almost halfway there. At <b>ff...</b> we are nearing the end. The fact that you can tell this rests on two principles: 1) you sort your jobs by their uuid and 2) UUIDs are random, as in, <a href="https://en.wikipedia.org/wiki/Uniform_distribution_(continuous)" target="_blank">distributed uniformly</a>.<br />
<br />
However, last week, I noticed a strange thing: a clearly visible pattern in the uuid column of a database table. It should be impossible, but there it was. It looked like this:<br />
<br />
<code style="font-family: "courier new" , "courier" , monospace;">
> SELECT uuid FROM example ORDER BY id;<br />
4f95de28-0fd1-48db-ad2e-34ecd169c483<br />
4331cb9e-1d91-11e9-be2c-45923c63e8a2<br />
4331cc4c-1d91-11e9-be2c-45923c63e8a2<br />
4331ccec-1d91-11e9-be2c-45923c63e8a2<br />
4331cd7e-1d91-11e9-be2c-45923c63e8a2<br />
c7e2f124-f6ba-4434-843f-89958a7436ec<br />
4331ce10-1d91-11e9-be2c-45923c63e8a2<br />
4331ce9e-1d91-11e9-be2c-45923c63e8a2<br />
4331cf28-1d91-11e9-be2c-45923c63e8a2<br />
4331cfaf-1d91-11e9-be2c-45923c63e8a2<br />
4331d017-1d91-11e9-be2c-45923c63e8a2<br />
4331d0c6-1d91-11e9-be2c-45923c63e8a2<br />
4331d139-1d91-11e9-be2c-45923c63e8a2<br />
4331d1a7-1d91-11e9-be2c-45923c63e8a2<br />
4331d20e-1d91-11e9-be2c-45923c63e8a2<br />
4331d271-1d91-11e9-be2c-45923c63e8a2<br />
4331d2d7-1d91-11e9-be2c-45923c63e8a2<br />
3e18f8dd-b1d3-4e16-8a81-4bdceac91772<br />
4331d33a-1d91-11e9-be2c-45923c63e8a2<br />
f6b8658d-846b-4418-a79c-713db516203e<br />
4331d3a8-1d91-11e9-be2c-45923c63e8a2<br />
4331d40d-1d91-11e9-be2c-45923c63e8a2<br />
4331d48e-1d91-11e9-be2c-45923c63e8a2<br />
4331d4fe-1d91-11e9-be2c-45923c63e8a2<br />
4331d567-1d91-11e9-be2c-45923c63e8a2<br />
4331d5cb-1d91-11e9-be2c-45923c63e8a2<br />
4331d630-1d91-11e9-be2c-45923c63e8a2<br />
4331d691-1d91-11e9-be2c-45923c63e8a2<br />
14a5cb4c-f336-4240-aff3-4ffcfa8d135f<br />
4331d6f9-1d91-11e9-be2c-45923c63e8a2<br />
4331d762-1d91-11e9-be2c-45923c63e8a2<br />
4331d7c8-1d91-11e9-be2c-45923c63e8a2<br />
4331d83d-1d91-11e9-be2c-45923c63e8a2<br />
</code>
<br />
<div>
<br /></div>
<br />
Hm, that is <b>very</b> weird. Did we maybe convert an old auto-incrementing integer column into UUIDs in a very stupid way? Did we maybe use UUID version 3 or 5? Did our library corrupt the first bits of the binary representation of the UUID? After a while, I remembered that we initialized this column like so:<br />
<br />
<blockquote class="tr_bq">
UPDATE example SET uuid = UUID() WHERE uuid IS NULL;</blockquote>
<br />
I also remembered reading this in the <a href="https://dev.mysql.com/doc/refman/8.0/en/miscellaneous-functions.html#function_uuid" target="_blank">MySQL documentation</a>:<br />
<blockquote class="tr_bq">
"Warning: Although UUID() values are intended to be unique, they are not necessarily unguessable or unpredictable. If unpredictability is required, UUID values should be generated some other way." </blockquote>
If you are like me, you won't use <span style="font-family: "courier new" , "courier" , monospace;">UUID()</span> for secrets after reading this (and I didn't!). If you are even more like me, you will think that this is like the difference between <a href="https://en.wikipedia.org/wiki//dev/random" target="_blank"><span style="font-family: "courier new" , "courier" , monospace;">/dev/urandom</span> and <span style="font-family: "courier new" , "courier" , monospace;">/dev/random</span></a> and that the uniform distribution law still applies here. However, to my great surprise, the UUIDs generated by <span style="font-family: "courier new" , "courier" , monospace;">UUID()</span> are not uniform at all! The result is that a significant portion of UUIDs in our database are not uniform:<br />
<br />
<code style="font-family: "courier new" , "courier" , monospace;">
> SELECT COUNT(*), LEFT(uuid, 1) FROM example GROUP BY LEFT(uuid, 1);<br />
+----------+---------------+<br />
| count(*) | LEFT(guid, 1) |<br />
+----------+---------------+<br />
| 1943 | 0 </code><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> |</span><br />
<code style="font-family: "courier new" , "courier" , monospace;">
| 1871 </code><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;">| 1 </span><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> |</span><br />
<code style="font-family: "courier new" , "courier" , monospace;">
| 1913</code><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> | 2 </span><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> |</span><br />
<code style="font-family: "courier new" , "courier" , monospace;">
| 1843</code><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> | 3 </span><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> |</span><br />
<code style="font-family: "courier new" , "courier" , monospace;">
| 3050</code><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> | 4 </span><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> |</span><br />
<code style="font-family: "courier new" , "courier" , monospace;">
| 1943 </code><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;">| 5 </span><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> |</span><br />
<code style="font-family: "courier new" , "courier" , monospace;">
| 1889 </code><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;">| 6 </span><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> |</span><br />
<code style="font-family: "courier new" , "courier" , monospace;">
| 1866 </code><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;">| 7 </span><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> |</span><br />
<code style="font-family: "courier new" , "courier" , monospace;">
| 1865 </code><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;">| 8 </span><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> |</span><br />
<code style="font-family: "courier new" , "courier" , monospace;">
| 1903 </code><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;">| 9 </span><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> |</span><br />
<code style="font-family: "courier new" , "courier" , monospace;">
| 1868 </code><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;">| a </span><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> |</span><br />
<code style="font-family: "courier new" , "courier" , monospace;">
| 1898 </code><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;">| b </span><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> |</span><br />
<code style="font-family: "courier new" , "courier" , monospace;">
| 1854 </code><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;">| c </span><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> |</span><br />
<code style="font-family: "courier new" , "courier" , monospace;">
| 1897 </code><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;">| d </span><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> |</span><br />
<code style="font-family: "courier new" , "courier" , monospace;">
| 1941 </code><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;">| e </span><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> |</span><br />
<code style="font-family: "courier new" , "courier" , monospace;">
| 1836 </code><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;">| f </span><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;">|</span><br />
<code style="font-family: "courier new" , "courier" , monospace;">
+----------+---------------+<br />
</code>
<br />
<div>
<br />
So, the lesson for today is: take warnings in documentation seriously. If you used <span style="font-family: "courier new" , "courier" , monospace;">UUID()</span> for data that is supposed to be secret (like passwords), you have a serious problem, as these secrets can now be easily guessed.<br />
<br />
Edit: read this <a href="https://blog.waleson.com/2019/02/uuids-in-mysql-follow-up.html" target="_blank">follow-up post</a></div>
Jouke Walesonhttp://www.blogger.com/profile/12215646682936082041noreply@blogger.comtag:blogger.com,1999:blog-4710497652927856789.post-72396392086797701262019-01-26T16:02:00.000+01:002019-01-26T17:22:31.418+01:00Highly Productive Teams and their Speed BumpsIn <a href="https://en.wikipedia.org/wiki/Civilisation_(TV_series)" target="_blank">Civilisation</a>, the fantastic BBC series from the 60's, Kenneth Clark is puzzled by the short burst of time in which cathedrals, spiritual movements and art styles are created. For a couple of decades (which sounds long by our standards), there seem to be surges in <b>productivity, creativity and <em>energy</em></b> in almost the entire population. More was accomplished in these bursts than in the centuries of relative inactivity before or after.<br />
<br />
In my experience, <b>product development is very comparable</b>. When looking back at some of the most important software projects in which I've played a part, there were bursts of just a couple of days where we were <em>incredibly</em> productive. We were energized, worked as one and were in a state of flow most of the time. The best part is this: although the experiences are very intense, they don't leave you very tired. Instead, afterwards you feel energized and you realize that you just did your best work! As a CTO, I would love my team to work like this!<br />
<br />
Is it possible to achieve this high productivity reliably? I often fear that our competitors are consistently operating at this breakneck speed, but actually I suspect that there are very, very few teams in the world that get close. So, perhaps it is simply not possible. Maybe my memory is playing tricks on me, maybe I'm just nostalgic, or maybe it's another case of the 80% / 20% law, where 80% of the work gets done in 20% of the time and the finishing touches are actually the hardest and most boring to get right. Nevertheless, I want to learn something from these high productivity moments and create the right environment for my team to experience them.<br />
<br />
These are the common denominators of the high-productivity bursts I've experienced, either when working solo or with a team:<br />
<ul>
<li>There is a <b>clear goal</b>: build X or solve problem Y.</li>
<li>Everyone should be <b>well-rested</b> (as in, not tired or hungover).</li>
<li>The team consists just of people with a <b>can-do attitude</b>, no negativity. One sour face severely impacts the rest of the team.</li>
<li>The members don't have to be close personal friends, but they absolutely need to <b>respect each other's skills</b>.</li>
<li><b>Autonomy</b>: everything that is needed to accomplish the project should be in one room and focused on the project. If more is needed, make sure that it's just one phone call away.</li>
<li>Everyone in the room can add something, if not, pair up with someone or get the team lunch or coffee, don't go browsing reddit like a zombie.</li>
<li>There is an organizer / tie-breaker that people look to for when they're stuck or have questions.</li>
<li>Leadership that acknowledges the importance of the project. Together with respect for each other's skills, this provides the necessary <a href="https://en.wikipedia.org/wiki/Psychological_safety" target="_blank">Psychological Safety</a> to the team</li>
<li>A <b>small team</b> of max 5 people. Coordination and keeping everyone focused becomes too hard when the team is bigger.</li>
</ul>
There are also "speed bumps" that make a high productivity environment impossible.<br />
<ul>
<li><b>No clear mission.</b></li>
<li><b>Negative team members.</b> Although there is lots of value in criticism and being cautious, especially when dealing with high performance or security requirements, voice these concerns in a positive way, or, when the project is just a Proof of Concept, say that we'll tackle it when going to production.</li>
<li>People that like talking about the project more than actually getting to work and building it.</li>
<li><b>External dependencies</b> outside of the room that are not immediately available, such as designers, the legal department or a product owner. Make sure you make everyone is available to the team. If you still have external dependencies, it can mean two things: 1) the goal or scope is actually not clear to the team or 2) the project doesn't have the backing of leadership.</li>
<li>No clear organizer, team members staring at each other waiting for others to make a move.</li>
</ul>
The most critical speed bump is "external dependencies". As soon as the creative process requires something that is not instantly available, productivity takes a huge hit. This is why productivity is so hard in larger organizations where the amount of stakeholders is very large.Jouke Walesonhttp://www.blogger.com/profile/12215646682936082041noreply@blogger.comtag:blogger.com,1999:blog-4710497652927856789.post-84529511223909391462018-10-07T23:54:00.000+02:002019-05-11T12:04:55.472+02:00Code Should be ReadableOn the first day of my first job, at age 23, I learned the most important lesson of my life about programming: <b>code should be readable</b>. Four years of CS at university did not prepare me for this, but one day of work did (thanks Achiel!).<br />
<br />
I had created something "clever" and was barked at by the Senior Engineer of the team that my solution was too complicated. For a second I thought he was suffering from low IQ, but he explained that:<br />
<br />
<ol>
<li>He was not able to <i>instantly</i> understand my code. <i><u>Lesson</u></i>: It was apparently <b>vital</b> that other people in the team understood <b>my </b>code. In fact, it was not even my<b> </b>code,<b> the team owned it</b>.</li>
<li>The problem I was solving was simple and not worth spending much mental capacity on. <i><u>Lesson</u></i>: <b>writing code is just a small part</b> of what happens with it, code is much more frequently read, tested, debugged and refactored. All these activities take time and mental capacity.</li>
<li>In 6 months, I would have forgotten the code and would look at it the same way he did now (that is: with a frown and raised eyebrows). <i><u>Lesson</u></i>: <b>You are temporarily suffering from understanding the code too well</b>, make sure you compensate for that.</li>
<li>We were paid (quite well) to make product for the company, not to be smart-asses. <u><i>Lesson</i></u>: <b>time is money</b> and there is something called <a href="https://en.wikipedia.org/wiki/Opportunity_cost">Opportunity Cost</a> that makes your time even more valuable. <b>Boring code is good code.</b></li>
</ol>
These lessons have stuck with me forever and made me allergic to complicated code. Of course sometimes problems really are complex, and there is big difference between "essential complexity" and "merely complicated". Senior Software Engineers should have a well-developed gut feeling to distinguish between the two.<br />
<br />
I don't care much for fanatic discussions about <b>Test Driven Development</b><i> </i>or <b>Micro Services vs. Monoliths</b> because reality is much less clear-cut. I think it's <b>MUCH</b> more important that whenever you create software, or basically anything*, that you keep this in mind:<br />
<ul>
<li>the complexity of the solution should match the complexity of the problem</li>
<li>what you create should be easy to understand for those who work with it</li>
</ul>
<div>
<br />
As long as you build your software by these two rules, <b>you should be alright.</b></div>
<div>
<b><br /></b></div>
<div>
<b><br /></b></div>
<div>
<b><br /></b></div>
<div>
<b><br /></b></div>
<div>
* read <a href="https://en.wikipedia.org/wiki/The_Design_of_Everyday_Things">The Design of Everyday things</a> by Don Norman and <a href="https://en.wikipedia.org/wiki/Don%27t_Make_Me_Think">Don't Make me Think</a> by Steve Krug</div>
Jouke Walesonhttp://www.blogger.com/profile/12215646682936082041noreply@blogger.com