<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:googleplay="http://www.google.com/schemas/play-podcasts/1.0"><channel><title><![CDATA[Frank Talk]]></title><description><![CDATA[Aging programmer Frank LaRosa rambles on about software, technology, politics, humor, and whatever else comes to mind. ]]></description><link>https://www.franklarosa.com</link><image><url>https://substackcdn.com/image/fetch/$s_!6ZbD!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe12d1e14-314a-450b-a380-30a151730e9b_1024x1024.png</url><title>Frank Talk</title><link>https://www.franklarosa.com</link></image><generator>Substack</generator><lastBuildDate>Tue, 07 Apr 2026 09:04:15 GMT</lastBuildDate><atom:link href="https://www.franklarosa.com/feed" rel="self" type="application/rss+xml"/><copyright><![CDATA[Frank LaRosa]]></copyright><language><![CDATA[en]]></language><webMaster><![CDATA[larosa@substack.com]]></webMaster><itunes:owner><itunes:email><![CDATA[larosa@substack.com]]></itunes:email><itunes:name><![CDATA[Frank LaRosa]]></itunes:name></itunes:owner><itunes:author><![CDATA[Frank LaRosa]]></itunes:author><googleplay:owner><![CDATA[larosa@substack.com]]></googleplay:owner><googleplay:email><![CDATA[larosa@substack.com]]></googleplay:email><googleplay:author><![CDATA[Frank LaRosa]]></googleplay:author><itunes:block><![CDATA[Yes]]></itunes:block><item><title><![CDATA[AI Is Coming For Our Jobs]]></title><description><![CDATA[The future of programming is finally here. Are we ready for it?]]></description><link>https://www.franklarosa.com/p/ai-is-coming-for-our-jobs</link><guid isPermaLink="false">https://www.franklarosa.com/p/ai-is-coming-for-our-jobs</guid><dc:creator><![CDATA[Frank LaRosa]]></dc:creator><pubDate>Sat, 14 Feb 2026 18:13:55 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!d_MU!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3fe94fee-39a4-475f-aee1-e0a6efd0ee31_1536x1024.heic" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!d_MU!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3fe94fee-39a4-475f-aee1-e0a6efd0ee31_1536x1024.heic" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!d_MU!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3fe94fee-39a4-475f-aee1-e0a6efd0ee31_1536x1024.heic 424w, https://substackcdn.com/image/fetch/$s_!d_MU!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3fe94fee-39a4-475f-aee1-e0a6efd0ee31_1536x1024.heic 848w, https://substackcdn.com/image/fetch/$s_!d_MU!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3fe94fee-39a4-475f-aee1-e0a6efd0ee31_1536x1024.heic 1272w, https://substackcdn.com/image/fetch/$s_!d_MU!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3fe94fee-39a4-475f-aee1-e0a6efd0ee31_1536x1024.heic 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!d_MU!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3fe94fee-39a4-475f-aee1-e0a6efd0ee31_1536x1024.heic" width="1456" height="971" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3fe94fee-39a4-475f-aee1-e0a6efd0ee31_1536x1024.heic&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:971,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:285529,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/heic&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.franklarosa.com/i/187971956?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3fe94fee-39a4-475f-aee1-e0a6efd0ee31_1536x1024.heic&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!d_MU!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3fe94fee-39a4-475f-aee1-e0a6efd0ee31_1536x1024.heic 424w, https://substackcdn.com/image/fetch/$s_!d_MU!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3fe94fee-39a4-475f-aee1-e0a6efd0ee31_1536x1024.heic 848w, https://substackcdn.com/image/fetch/$s_!d_MU!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3fe94fee-39a4-475f-aee1-e0a6efd0ee31_1536x1024.heic 1272w, https://substackcdn.com/image/fetch/$s_!d_MU!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3fe94fee-39a4-475f-aee1-e0a6efd0ee31_1536x1024.heic 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>My first large-scale experiment with AI programming didn&#8217;t go so well. I used Claude Code and told it to generate an Android version of an iOS app. About 90 minutes later, it had produced a passable version of the app&#8217;s home screen, but nothing else. I had to redirect it several times to fix errors and get something that would build. It cost me $67.</p><p>I&#8217;d say it failed to give me a commercially useful result, but I learned a lot. Later that day I was using it easily to do smaller incremental tasks. Then I signed up for a company account and invited my team.</p><p>AI is coming for our jobs. That&#8217;s the prediction, the fear, the worry that we hear every day. Some say it&#8217;s here already, others will tell you it&#8217;s coming in so many months or years. The closer you are to the technology, the sooner you expect it to disrupt everything. Is it for real?</p><p>My company writes software as a service. I work with dozens of business customers so I have access to a wide range of input. I don&#8217;t yet have any customer who is outright using AI to replace our services. Most are more interested in putting AI in their products or on their web sites. A few will ask ChatGPT for bits of advice and pass them on to me. I use ChatGPT too, so I have access to the same information.</p><p>I don&#8217;t doubt the day will come when non-programmers will be able to build viable applications without coding. But there are different kinds of non-programmers. A competent and detail-oriented worker who understands their business should get good results. An executive who&#8217;s used to barking out vague commands to their underlings isn&#8217;t going to get what they want.</p><p>This leaves a window for us to take advantage. Right now people still need a developer to guide the AI tools, give them coherent instructions, check their results. If you code, you should absolutely have these tools at your disposal and use them. Generating complete applications from scratch, while possible, isn&#8217;t the only way to go. You can ask for individual features, review them, and make sure you understand them before you accept the code. Even if you&#8217;re not using AI for codewriting you should at least be using it for code review or to do mindless refactoring jobs.</p><p>I don&#8217;t think larger and more conservative businesses are getting ready to replace their entire coding teams with AI. When a business is using technology, rather than selling it as a product, computers are a means to an end, and technological progress isn&#8217;t always a priority. I&#8217;ve run into plenty of cases where decades-old equipment and software were happily being used on a daily basis. Everything evolves eventually, and we must be prepared for the future, but we shouldn&#8217;t ignore the present either.</p><p>Long term? We&#8217;ve trained AI to be good at writing software, but it begs the question of whether much of the software we use today will even make sense in a future of ubiquitous AI. Why do you need a calendar app when you can just ask your AI assistant to remember your appointments? Why do you need tax preparation software when you can just say, &#8220;Do my taxes&#8221;? Will IDEs, filled with screens and options and plug-ins, be necessary when AI is writing most of our code?</p><p>Will we even need need languages like Swift, Java, or Kotlin when AI can spit out the executable code directly? Even the elite developers who make our most important programming tools aren&#8217;t safe.</p><p>I like writing software. I&#8217;ve been doing it more than four decades. Even if I have AI writing code for me at work, I&#8217;ll still maintain my personal applications by hand, much like a woodworker is going to keep building furniture in his garage that cannot compete with the flat-pack fiberboard from Ikea. Even then, I&#8217;ll at least point my AI tools at my code and ask them to review it. Not to save time, but to see what I can learn.</p><p>Technology is change. We might not like it, but that&#8217;s what we signed up for when we became software developers. If I hadn&#8217;t been willing to change, I&#8217;d still be writing BASIC programs on a TRS-80. Or more likely, sitting in a bar somewhere complaining about how much better the world was when I was writing BASIC programs on a TRS-80. That ain&#8217;t for me.</p><p>Maybe society will collapse when we all lose our jobs to AI. Maybe we&#8217;ll lose our retirement savings when the AI companies fail. Whatever happens, few of us are in any position to change it. You&#8217;re either prepared for it or you&#8217;re not. I&#8217;ll face it with my eyes open.</p>]]></content:encoded></item><item><title><![CDATA[Make Apps Great Again]]></title><description><![CDATA[Stand up for the craft of programming and fight scammy, bloated software]]></description><link>https://www.franklarosa.com/p/make-apps-great-again</link><guid isPermaLink="false">https://www.franklarosa.com/p/make-apps-great-again</guid><dc:creator><![CDATA[Frank LaRosa]]></dc:creator><pubDate>Tue, 06 May 2025 14:01:14 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/d03a42f6-e928-48d4-99b2-6b2eca3313fa_1536x1024.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!SDXn!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf06a451-6e71-424a-98ef-fd96f65e9ff2_1536x1024.heic" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!SDXn!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf06a451-6e71-424a-98ef-fd96f65e9ff2_1536x1024.heic 424w, https://substackcdn.com/image/fetch/$s_!SDXn!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf06a451-6e71-424a-98ef-fd96f65e9ff2_1536x1024.heic 848w, https://substackcdn.com/image/fetch/$s_!SDXn!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf06a451-6e71-424a-98ef-fd96f65e9ff2_1536x1024.heic 1272w, https://substackcdn.com/image/fetch/$s_!SDXn!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf06a451-6e71-424a-98ef-fd96f65e9ff2_1536x1024.heic 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!SDXn!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf06a451-6e71-424a-98ef-fd96f65e9ff2_1536x1024.heic" width="1456" height="971" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/cf06a451-6e71-424a-98ef-fd96f65e9ff2_1536x1024.heic&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:971,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:361576,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/heic&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.franklarosa.com/i/162935423?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf06a451-6e71-424a-98ef-fd96f65e9ff2_1536x1024.heic&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!SDXn!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf06a451-6e71-424a-98ef-fd96f65e9ff2_1536x1024.heic 424w, https://substackcdn.com/image/fetch/$s_!SDXn!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf06a451-6e71-424a-98ef-fd96f65e9ff2_1536x1024.heic 848w, https://substackcdn.com/image/fetch/$s_!SDXn!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf06a451-6e71-424a-98ef-fd96f65e9ff2_1536x1024.heic 1272w, https://substackcdn.com/image/fetch/$s_!SDXn!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf06a451-6e71-424a-98ef-fd96f65e9ff2_1536x1024.heic 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>We call them apps instead of applications because they&#8217;re supposed to add small, incremental bits of functionality to your phone. It used to be that you could go to the App store and buy modest but useful apps for a few dollars, and start using them immediately, almost as you would use a web site.</p><p>But things went sideways pretty fast. Today, apps try to shove in as many features as possible in an attempt to keep up with their competitors. If you just need something simple, you might have to wade through dozens of screens to find it.</p><p>And it gets worse. Every app asks you to create an account. Every app wants you to subscribe. I'm all for paying a fair price for a piece of software, but I'm not going to "subscribe" to a calculator app that doesn't provide any ongoing new material. I don't want to create an account if the only reason for doing so is to give the app developer a way to send me spam.</p><p>Here is my manifesto: the principles I'll use whenever I develop my own apps. I encourage all good and noble programmers out there to adopt them for yourselves.</p><h3><strong>No Servers</strong></h3><p>Does your app really need a server? It might, if it has to share data with other users, or access information that isn't directly available on the internet. But if it can work without a server, it should. Store data in local databases or local files. Use cloud storage to back it up or make it available to multiple devices.</p><h3>No Accounts</h3><p>Does your app require that users have accounts? If you aren't building an app that is part of an existing web service, it probably doesn&#8217;t. Even if your app has a server, you can generate a random account and store the credentials in the user's cloud storage or keychain &#8212; allowing them to use the app and access their account from multiple devices, without having to surrender their email address or remember a password.</p><h3>No Internet</h3><p>If your app does use internet services, it shouldn't be dependent on them to the extent that it becomes useless (or crashes) when they aren&#8217;t available. Every app should expect internet outages and plan for them. Use local data stores to download or cache needed data in advance, and stage outgoing data for delivery later. If you are using a remote database, learn how to synchronize it with a local store in your app. Tip: Avoid autogenerated keys. Use UUIDs or natural keys instead.</p><h3>No Subscriptions</h3><p>Asking people to subscribe to an app just to keep using it is a shameful money grab. If your app does a fixed thing, charge a fixed price. If you want people to be able to install your app for free and pay later, then offer a genuinely useful tier of free functionality and a one-time purchase price to get the rest. Obviously there are exceptions for apps that provide streaming content or other ongoing services that cost money to produce. But, you are not Adobe. You don&#8217;t employ hundreds of developers and release major new versions twice a year.</p><h3>No Ads</h3><p>If your app has to stop and show the user a full-screen ad, you've immediately cut its usefulness in half. If they were considering paying for your app before, the ad is more likely going to motivate them to delete it. Ads are obsolete &#8212; they don't make any useful amount of money. Find some other way to earn revenue if you need the revenue.</p><h3>Put the User First</h3><p>Users want to open your app and start using it. If your app hits them with an immediate solicitation for money or a request for personal information, you aren't serving the user's needs, you're serving your own needs. Build good software that people want, and they'll pay you for it, without your needing to trick them or strong-arm them into doing it.</p><p>App stores used to be good at enforcing these kinds of rules, but over time, they've given in to greed. Don't let that deter you. Be a better developer, produce a better app. Rise above the fray. Make apps great again, starting with yours.</p>]]></content:encoded></item><item><title><![CDATA[The Ultimate State Capitol Road Trip]]></title><description><![CDATA[A brute-force solution to the traveling salesman problem]]></description><link>https://www.franklarosa.com/p/the-optimal-48-state-capital-road</link><guid isPermaLink="false">https://www.franklarosa.com/p/the-optimal-48-state-capital-road</guid><dc:creator><![CDATA[Frank LaRosa]]></dc:creator><pubDate>Sat, 29 Jun 2024 20:33:39 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!Clyh!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc47b016a-b94f-43b3-811c-3737ca06a686_2100x1500.heic" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Clyh!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc47b016a-b94f-43b3-811c-3737ca06a686_2100x1500.heic" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Clyh!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc47b016a-b94f-43b3-811c-3737ca06a686_2100x1500.heic 424w, https://substackcdn.com/image/fetch/$s_!Clyh!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc47b016a-b94f-43b3-811c-3737ca06a686_2100x1500.heic 848w, https://substackcdn.com/image/fetch/$s_!Clyh!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc47b016a-b94f-43b3-811c-3737ca06a686_2100x1500.heic 1272w, https://substackcdn.com/image/fetch/$s_!Clyh!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc47b016a-b94f-43b3-811c-3737ca06a686_2100x1500.heic 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Clyh!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc47b016a-b94f-43b3-811c-3737ca06a686_2100x1500.heic" width="1456" height="1040" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c47b016a-b94f-43b3-811c-3737ca06a686_2100x1500.heic&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1040,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:285427,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/heic&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Clyh!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc47b016a-b94f-43b3-811c-3737ca06a686_2100x1500.heic 424w, https://substackcdn.com/image/fetch/$s_!Clyh!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc47b016a-b94f-43b3-811c-3737ca06a686_2100x1500.heic 848w, https://substackcdn.com/image/fetch/$s_!Clyh!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc47b016a-b94f-43b3-811c-3737ca06a686_2100x1500.heic 1272w, https://substackcdn.com/image/fetch/$s_!Clyh!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc47b016a-b94f-43b3-811c-3737ca06a686_2100x1500.heic 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Recently I read a <a href="https://randalolson.com/2016/06/05/computing-optimal-road-trips-on-a-limited-budget/">blog post</a> from Dr. Randal S. Olsen in which he plotted an optimal road trip to each of the lower 48 state capitols, solving what is commonly known as the &#8220;traveling salesman&#8221; problem. We consider this problem impossible to solve definitively, but it&#8217;s certainly possible to create algorithms that give a pretty good approximation. Olsen&#8217;s circular route is 13,310 miles in length. Could I do better? I thought it would be fun to try.</p><p>I&#8217;m not that great at math. So for me, an elegant algorithm full of tricky calculations wasn&#8217;t going to be the way. I needed something I could understand.</p><h2>Enter Brute Force</h2><p>&#8220;Brute force&#8221; is a term we use to describe programming that&#8217;s inefficient or inelegant. If it works at all, it&#8217;s only because computers are fast enough that it doesn&#8217;t matter. Brute force programming is easy to write because it works the way an average person thinks &#8212; bubble sort is the classic example. So what&#8217;s the brute force solution to this problem? Just calculate every possible route and sort them to find the shortest. Easy!</p><p>Easy, until it sinks in that the number of possible routes is 48!, approximately 1.24&#215;10^61. To call this number large is an understatement. By comparison, there are only around 10^56 atoms in the solar system. If my computer could process a trillion routes per second, it would not even make a dent in the problem before the sun exploded. So that ain&#8217;t going to work.</p><p>What about computing routes completely at random, keeping track of the shortest one found so far? I didn&#8217;t think it would work but I was curious how close it could get. So I got a list of GPS coordinates to the capital cities and calculated the distances between each (actual math!). I decided to use the straight-line distance between the cities as an approximation and plot the result on Google Maps later to get the true driving distance.</p><p>I coded it up and ran it. Of course it spit out some huge numbers at first, 30,000+ miles. As it ran, it started finding shorter routes. I let it run overnight. By the next morning I was looking at routes near the 20k mark for distance (and I knew that the actual road route would be longer once I plotted it). I did some code optimizations but soon realized this was not going to give me the answer I wanted. </p><h2>Brute Force, but Refined</h2><p>I decided to revisit the idea of brute force, which I described earlier as going about the problem the way a person would. So how would a person do this? They are probably going to pick a starting point and then just go to the next closest place. Since there are only 48 starting points, it was easy to try this, and easy to see that it wasn&#8217;t the solution. When I examined Dr. Olsen&#8217;s map I could see that there are many cases where the route doesn&#8217;t in fact take you to the next closest city. A good example of this occurs in the area around Albany, which acts as a bottleneck between New England and the rest of the country. </p><p>Still, it was obvious that each city was at least connected to a nearby city. There&#8217;s no chance that an optimal route is going to have you drive from Boston to Little Rock in one shot. I changed my random route generator to pick one of the five closest points as the next destination. I added some additional optimizations such as bailing out early once the route exceeded the best found so far. I let it fly.</p><p>This worked much better. I got routes under 20,000 miles pretty fast. After a couple of hours it came up with what I call route #10639, because it has a straight-line length of 10,639 miles. When I plotted this on Google Maps and added up the driving distances, I got 13,055 miles, 255 miles better than the goal. Success!</p><p>Over the next week I made various changes to my code and ran it for a lot more hours. None of those runs produced a shorter route, and in fact most of them ended up reproducing route #10639. </p><h2>The Optimal Route</h2><p>Here&#8217;s a map view of the winning route:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!1EpD!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe14dc40d-003d-426b-8061-b7310bb8eaf0_2618x1298.heic" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!1EpD!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe14dc40d-003d-426b-8061-b7310bb8eaf0_2618x1298.heic 424w, https://substackcdn.com/image/fetch/$s_!1EpD!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe14dc40d-003d-426b-8061-b7310bb8eaf0_2618x1298.heic 848w, https://substackcdn.com/image/fetch/$s_!1EpD!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe14dc40d-003d-426b-8061-b7310bb8eaf0_2618x1298.heic 1272w, https://substackcdn.com/image/fetch/$s_!1EpD!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe14dc40d-003d-426b-8061-b7310bb8eaf0_2618x1298.heic 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!1EpD!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe14dc40d-003d-426b-8061-b7310bb8eaf0_2618x1298.heic" width="1456" height="722" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e14dc40d-003d-426b-8061-b7310bb8eaf0_2618x1298.heic&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:722,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:550838,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/heic&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!1EpD!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe14dc40d-003d-426b-8061-b7310bb8eaf0_2618x1298.heic 424w, https://substackcdn.com/image/fetch/$s_!1EpD!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe14dc40d-003d-426b-8061-b7310bb8eaf0_2618x1298.heic 848w, https://substackcdn.com/image/fetch/$s_!1EpD!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe14dc40d-003d-426b-8061-b7310bb8eaf0_2618x1298.heic 1272w, https://substackcdn.com/image/fetch/$s_!1EpD!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe14dc40d-003d-426b-8061-b7310bb8eaf0_2618x1298.heic 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><a href="https://www.google.com/maps/d/u/0/edit?mid=16Plg4xXS5RgWFTdVva5vLbJ1HCRaK68&amp;usp=sharing">View the result on Google Maps</a> to get a closer look. </p><p>For much of the route we&#8217;re traveling along the coasts or the national borders, as you might expect. The hard part is knowing when and in what order to visit the interior cities. In this solution, we cover 11 of them in a detour between Montgomery and Jackson, and the rest between Helena and Bismarck. It&#8217;s a far from obvious route, yet it emerged as the shortest among several billion alternatives.</p><p>To get this result we had to make the non-intuitive choice, on several occasions, to bypass nearby cities for more distant destinations. Indianapolis and Frankfort are only around 200 miles from their counterparts in Ohio and West Virginia, yet we don&#8217;t visit them until thousands of miles later. Harrisburg eschews its three closest neighbors, routing you to Charleston or Albany depending on your direction of travel. The detours to the interior cities could have started anywhere along the northern or southern borders.</p><p>There were two runners-up that are worth a look. <a href="https://www.google.com/maps/d/u/0/edit?mid=1i_NEwN8eFIbm3_OVZ3EM4LtizEd-vLQ&amp;usp=sharing">Route #10652</a> is 13,132 miles, and <a href="https://www.google.com/maps/d/u/0/edit?mid=1dRgSIOShrKSQPLTYdcYfQNKcxf2OZ2s&amp;usp=sharing">route #10676</a> is 13,166, both shorter than the goal<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-1" href="#footnote-1" target="_self">1</a>. It&#8217;s interesting to compare the maps and see where they differ. Route 10652 sends you from Santa Fe to Oklahoma City instead of Austin. In 10676 we see one change in New England when visiting the three northernmost cities. </p><h1>Some Technical Details</h1><p>I used an M1 Mac and wrote my program in Java. Since the M1 has 8 cores, I ran 8 threads. It&#8217;s easy to do this because the threads only have to interact with each other to decide who&#8217;s currently got the best solution. I can check around 250,000 routes per second with this arrangement. ChatGPT kindly provided a list of the GPS coordinates I needed, and a function to compute the distances between them.</p><p>Each route begins at a random location and branches out from there, choosing from a list of the next N nearest destinations (omitting those we&#8217;ve already visited). I tried various values of N between 2 and 16. N=8 produces the winning route most often. If the number is too small, we miss the cases where the route bypasses a lot of nearby cities; too large, and we waste too much time looking at bad solutions. I think it might make sense to have N be dynamic and get smaller as the route is nearing its end, but I haven&#8217;t tried it.</p><p>Is there a shorter route? My intuition says that if there is, I&#8217;d have found it. Perhaps you will implement my algorithm, or something like it, and prove me wrong.</p><h1>Final Thoughts</h1><p>We all have computers that sit idle when we aren&#8217;t working. Long running algorithms like the one I describe here are a good way to put those resources to use for little or no extra cost<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-2" href="#footnote-2" target="_self">2</a>.</p><p>Most of the software problems we solve in our day to day work require fast response times, and I don&#8217;t want to discourage you from looking up and using established algorithms to solve them. But I also want you to keep in mind that there&#8217;s still room in software development for novel solutions. Don&#8217;t fall into the rut of always settling for the standard way of doing things. Someone is going to invent the next great computer algorithm that&#8217;ll change the world, and it might as well be you.</p><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-1" href="#footnote-anchor-1" class="footnote-number" contenteditable="false" target="_self">1</a><div class="footnote-content"><p>When I mapped these, I used the city names rather than the addresses of the capitol buildings, so the measurements aren&#8217;t exactly equivalent, but the difference is trivial.</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-2" href="#footnote-anchor-2" class="footnote-number" contenteditable="false" target="_self">2</a><div class="footnote-content"><p>An 8-core Apple M1 or M2 consumes around 30 watts at full load, which costs 50 cents per hour where I live.</p></div></div>]]></content:encoded></item><item><title><![CDATA[If Goofus and Gallant were programmers]]></title><description><![CDATA[What if the misfit pair wrote code?]]></description><link>https://www.franklarosa.com/p/if-goofus-and-gallant-were-programmers</link><guid isPermaLink="false">https://www.franklarosa.com/p/if-goofus-and-gallant-were-programmers</guid><dc:creator><![CDATA[Frank LaRosa]]></dc:creator><pubDate>Thu, 28 Apr 2022 14:35:00 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!VWN2!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27307bf6-a7d2-4c02-b3b9-9c03ea5dd028_1849x750.heic" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!VWN2!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27307bf6-a7d2-4c02-b3b9-9c03ea5dd028_1849x750.heic" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!VWN2!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27307bf6-a7d2-4c02-b3b9-9c03ea5dd028_1849x750.heic 424w, https://substackcdn.com/image/fetch/$s_!VWN2!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27307bf6-a7d2-4c02-b3b9-9c03ea5dd028_1849x750.heic 848w, https://substackcdn.com/image/fetch/$s_!VWN2!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27307bf6-a7d2-4c02-b3b9-9c03ea5dd028_1849x750.heic 1272w, https://substackcdn.com/image/fetch/$s_!VWN2!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27307bf6-a7d2-4c02-b3b9-9c03ea5dd028_1849x750.heic 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!VWN2!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27307bf6-a7d2-4c02-b3b9-9c03ea5dd028_1849x750.heic" width="1456" height="591" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/27307bf6-a7d2-4c02-b3b9-9c03ea5dd028_1849x750.heic&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:591,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:281320,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/heic&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!VWN2!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27307bf6-a7d2-4c02-b3b9-9c03ea5dd028_1849x750.heic 424w, https://substackcdn.com/image/fetch/$s_!VWN2!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27307bf6-a7d2-4c02-b3b9-9c03ea5dd028_1849x750.heic 848w, https://substackcdn.com/image/fetch/$s_!VWN2!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27307bf6-a7d2-4c02-b3b9-9c03ea5dd028_1849x750.heic 1272w, https://substackcdn.com/image/fetch/$s_!VWN2!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27307bf6-a7d2-4c02-b3b9-9c03ea5dd028_1849x750.heic 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><a href="https://en.wikipedia.org/wiki/Goofus_and_Gallant">Goofus and Gallant</a> grew up to be software developers.</p><p>Gallant is a consummate professional. He studies project requirements carefully, asks a lot of questions, and spends plenty of time planning his approach before he starts programming. Gallant says that getting things right up front will save a lot of time in the long run.</p><p>Goofus is a maverick. He jumps in and starts coding right away. Goofus figures he can always go back and fix the code later if it doesn't work out.</p><p>Gallant believes in automated testing. He writes unit tests and integration tests, and sets up a build server to run them. He prepares test scripts for the QA team to use. Goofus says that testing is the customer's responsibility.</p><p>Gallant takes advantage of source control. He always makes a new branch when he works on a feature. Gallant spends a lot of time managing his branches and resolving merge conflicts, but he likes to be sure that his code is well tested before it gets into the product. Goofus saves time by writing all his code on the main branch. </p><p>Gallant adopts the latest software management practices. He divides his time into sprints and uses his company's project management software to document his work plan and progress. After each sprint he takes time to review his results and adjust his estimates for the next one. Goofus jots down a rough list of tasks on a notepad and keeps it on his desk while he works.</p><p>Gallant's project is six months late. He has many uncomfortable meetings with his boss during which the economics of software development are discussed. Gallant is steadfast in his belief that good programming practices are essential and that quality software development takes time. He tells his boss, "If you don't have time to do it right, when will you have time to do it over?"</p><p>Goofus enters a hack-a-thon contest. He wins first prize by coding a new social media app in 24 hours. Goofus quits his job and uses the prize money to start up a company to bring his app to market. The developer who takes over his old job complains that his code is a mess and will need to be rewritten from scratch.</p><p>Gallant's project is finally finished, but his company soon announces a round of layoffs. Gallant's name is at the top of the list.</p><p>Goofus raises 20 million dollars and takes his company public. He retires from programming and uses the money to hire a team to finish his app. When Gallant shows up for the job interview they hire him right away because he seems like just the kind of developer they need to get their software under control. Goofus spends most of his time meeting with investors. He does television interviews in which he attributes his success to dedication and hard work.</p><p>Gallant tells his new boss that their team needs more process. Getting things right up front will save a lot of time in the long run.</p>]]></content:encoded></item><item><title><![CDATA[Real Programmers]]></title><description><![CDATA[In my day, programmers wrote code.]]></description><link>https://www.franklarosa.com/p/real-programmers</link><guid isPermaLink="false">https://www.franklarosa.com/p/real-programmers</guid><dc:creator><![CDATA[Frank LaRosa]]></dc:creator><pubDate>Thu, 27 Jan 2022 15:26:00 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!w-8u!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F99bdfd8d-bbb2-42d6-bf02-c72c2034d018_1240x757.heic" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!w-8u!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F99bdfd8d-bbb2-42d6-bf02-c72c2034d018_1240x757.heic" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!w-8u!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F99bdfd8d-bbb2-42d6-bf02-c72c2034d018_1240x757.heic 424w, https://substackcdn.com/image/fetch/$s_!w-8u!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F99bdfd8d-bbb2-42d6-bf02-c72c2034d018_1240x757.heic 848w, https://substackcdn.com/image/fetch/$s_!w-8u!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F99bdfd8d-bbb2-42d6-bf02-c72c2034d018_1240x757.heic 1272w, https://substackcdn.com/image/fetch/$s_!w-8u!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F99bdfd8d-bbb2-42d6-bf02-c72c2034d018_1240x757.heic 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!w-8u!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F99bdfd8d-bbb2-42d6-bf02-c72c2034d018_1240x757.heic" width="1240" height="757" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/99bdfd8d-bbb2-42d6-bf02-c72c2034d018_1240x757.heic&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:757,&quot;width&quot;:1240,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:124474,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/heic&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!w-8u!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F99bdfd8d-bbb2-42d6-bf02-c72c2034d018_1240x757.heic 424w, https://substackcdn.com/image/fetch/$s_!w-8u!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F99bdfd8d-bbb2-42d6-bf02-c72c2034d018_1240x757.heic 848w, https://substackcdn.com/image/fetch/$s_!w-8u!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F99bdfd8d-bbb2-42d6-bf02-c72c2034d018_1240x757.heic 1272w, https://substackcdn.com/image/fetch/$s_!w-8u!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F99bdfd8d-bbb2-42d6-bf02-c72c2034d018_1240x757.heic 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">The author&#8217;s first computer, a TRS-80 Model III, running an early version of his Searchlight BBS software.</figcaption></figure></div><p>Once upon a time, real programmers wrote code. We had a computer, a C compiler, and a couple of books full of algorithms. The books never had all the answers so we invented our own solutions. We dug in and figured it out.</p><p>Today we "leverage" code. We make a dependency file and list out the software we want to use. Before we write a line of code ourselves, our project has inherited hundreds of third-party packages that promise to do everything we need. What is all this code? Where does it come from? Nobody seems to care.</p><p>We're convinced that we're saving time this way. Our managers are convinced they're saving money. In reality, we're just trading one problem for another. Don't know how to write a database query, send a push notification, schedule a batch job? Instead of spending hours learning how to actually do these things, you can spend hours installing a package from a sketchy web site, written by an anonymous graduate student, and figuring out how to use it by reading Stack Overflow posts.</p><p>In a couple of years your application will quit working. There will be an operating system change, a new security requirement, or a web service that no longer responds. The graduate student, now a manager at a biotech firm, will have long since abandoned the project. The code they left behind is incomprehensible. You'll be forced to start over with... a different package.</p><p>Real programmers are still around. We have better computers now, and we&#8217;ve learned the latest languages. We write code if anyone lets us. But we are sensitive. We don't like to be called names like "old-fashioned" or "obsolete". So we try, as best we can, to go with the program and use the packages. We want to keep our jobs.</p><p>One day society will collapse. Without access to package repositories and web sites, we'll be the only ones left who can program a computer. We'll be the heroes who rebuilt civilization. Admittedly, it's more likely that we'll be the first ones to die after the supply of frozen burritos and Starbucks coffee runs out.</p><p>Until then, when nobody is looking, we break out the C compiler and keep the tradition alive. We open a text editor. The code flows.</p>]]></content:encoded></item><item><title><![CDATA[ORM Considered Harmful?]]></title><description><![CDATA[Think for yourself, write your own code]]></description><link>https://www.franklarosa.com/p/orm-considered-harmful</link><guid isPermaLink="false">https://www.franklarosa.com/p/orm-considered-harmful</guid><dc:creator><![CDATA[Frank LaRosa]]></dc:creator><pubDate>Tue, 25 Jan 2022 16:44:00 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!2fve!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F61424eb3-6650-412c-91a8-f5a945229673_490x487.heic" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!2fve!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F61424eb3-6650-412c-91a8-f5a945229673_490x487.heic" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!2fve!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F61424eb3-6650-412c-91a8-f5a945229673_490x487.heic 424w, https://substackcdn.com/image/fetch/$s_!2fve!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F61424eb3-6650-412c-91a8-f5a945229673_490x487.heic 848w, https://substackcdn.com/image/fetch/$s_!2fve!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F61424eb3-6650-412c-91a8-f5a945229673_490x487.heic 1272w, https://substackcdn.com/image/fetch/$s_!2fve!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F61424eb3-6650-412c-91a8-f5a945229673_490x487.heic 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!2fve!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F61424eb3-6650-412c-91a8-f5a945229673_490x487.heic" width="490" height="487" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/61424eb3-6650-412c-91a8-f5a945229673_490x487.heic&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:487,&quot;width&quot;:490,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:34387,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/heic&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!2fve!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F61424eb3-6650-412c-91a8-f5a945229673_490x487.heic 424w, https://substackcdn.com/image/fetch/$s_!2fve!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F61424eb3-6650-412c-91a8-f5a945229673_490x487.heic 848w, https://substackcdn.com/image/fetch/$s_!2fve!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F61424eb3-6650-412c-91a8-f5a945229673_490x487.heic 1272w, https://substackcdn.com/image/fetch/$s_!2fve!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F61424eb3-6650-412c-91a8-f5a945229673_490x487.heic 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>I don&#8217;t buy object-relational mapping, at least not in Java. It's not because JPA or its implementations aren't well written, but because the concept itself robs capability in exchange for conveniences we don't really need.</p><p>Relational databases have a lot to offer. SQL is a rich language that can perform complex queries, bulk data updates, computations, summaries, and more. It can address not just rows but individual columns, and it can join tables even if they weren't formally set up to be joined. By contrast, ORM takes a limited view of the database as a series of objects to be manipulated individually and atomically. Create, read, update, delete.</p><p>When I interview a Java developer with an ORM background, I ask them to explain how they'd do some specific things:</p><ul><li><p>Update one property of an entity, or a series of entities that meet a criteria. I'm looking for a solution that does not involve reading the entire entity from the database, updating the property in memory, and writing the entire entity back.</p></li><li><p>Produce a report that involves grouping and summarizing data (for example, total sales per region for a given quarter). I prefer solutions that don't involve creating a report-specific Java class for every such report.</p></li><li><p>Read a row within a transaction in such a way that its data is guaranteed not to change for the duration of the transaction. What I want is the equivalent of the SELECT ... FOR UPDATE statement in SQL.</p></li></ul><p>You can accomplish these things with JPA, but the majority of developers don&#8217;t know how. So, what else can we use if not ORM? Surely we can't just go back to using SQL queries and plain old JDBC, right?</p><h2>Taming JDBC</h2><p>JDBC is ridiculous. You get a connection, create a statement, apply parameters, execute it, and iterate over a result set. You pull properties from the result one at a time and do something with them. Everything throws an exception, and everything must be carefully closed to avoid resource leaks. You end up with 20 or 30 lines of code to do one SQL query. If we did that for every query, we'd have an endless morass of code on our hands.</p><p>But our job as programmers is to simplify, refactor, and reuse. In a production application, we can hide the details of JDBC in our own functions. We can use generics, callbacks, reflection, and other language facilities to reduce the amount of JDBC overhead per query. I have done just this on many projects.</p><p>I won't present my code in detail because what I wrote is specific to my projects. It is intended to be a design pattern rather than a reusable library. But I'll show some of the end results in a moment.</p><h2>Result Mapping</h2><p>Building objects from database results is a mainstay of Enterprise Java. We assume that without an ORM, we'd be reduced to writing mountains of tedious, error-prone code like this:</p><pre><code><code>customerID = resultSet.getInt("customer_id");
customerName = resultSet.getString("customer_name");
customerEmail = resultSet.getString("customer_email");</code></code></pre><p>In fact, it's pretty easy to automate these assignments using Java reflection. You can infer the property names from the column names, or write your own annotations to customize them. I typically put the functionality into the base class of my model objects, and access it via a constructor that takes a ResultSet.</p><p>Yes, it's some effort, but it's the kind of thing you can do once and reuse for an entire project, or even an entire organization. You can make it work exactly the way you want, and you can easily override the default behavior and process ResultSets manually when necessary.</p><h2>Putting It All Together</h2><p>Here's an example of what a data access function might look like:</p><pre><code><code>public List&lt;Appointment&gt; getAppointments(int patientId) throws SQLException {
    String sql = "SELECT * FROM appointment WHERE patient_id=?";
    return this.queryForList(sql, Appointment.class, patientId);
}</code></code></pre><p>There's nothing here that isn't straightforwardly related to the task at hand. It works because the "queryForList" function encapsulates the complexity of JDBC: it knows how to create a Statement out of the SQL string, how to apply the paramters (passed as Varags so more than one is possible), and how to create instances of the indicated class from a ResultSet. It does all of this using code I wrote myself, without relying on an ORM tool.</p><p>What if the result doesn't map one for one with a Java object? In these cases, I use a different function that has a callback. It gives me access to the ResultSet without having to manage any of the other JDBC objects:</p><pre><code><code>public Map&lt;Integer,BigDecimal&gt; getTotalsByProduct() throws SQLException {
    String sql = 
"SELECT product_id, SUM(amount) as amount FROM receipts GROUP BY product_id";

    Map&lt;Integer,BigDecimal&gt; totals = new HashMap&lt;&gt;();

    this.queryWithPopulator(sql, new Populator() {
&#9;@Override
&#9;public void nextResult(ResultSet rs) throws Exception {
&#9;    totals.put(rs.getInt("product_id"),
              rs.getBigDecimal("amount"));
&#9;}
    });

    return totals;
}</code></code></pre><p>I could have solved the problem by creating a Java object with "productId" and "amount" properties, but I didn't really need any such object, so I chose to put the results directly into a HashMap. I use the same technique to handle one-to-many relationships (inner JOINs) by writing code that creates a new object only when the ID of the outer table changes.</p><h2>Think For Yourself, Write Your Own Code</h2><p>The methods outlined here are not theoretical. I've used them sucessfully in large, professional projects. I also work with ORM when I'm on teams where ORM was chosen by another architect, so I appreciate what it can do. But I feel more comfortable knowing that I can write any SQL statement at any time, and process its ResultSet any way I want. Programming is about writing code, and sometimes that means building up our own code libraries that best suit our projects. We should not shy away from it.</p>]]></content:encoded></item><item><title><![CDATA[PalmOS was a missed opportunity]]></title><description><![CDATA[A nine-year waste of time and money]]></description><link>https://www.franklarosa.com/p/why-palmos-sucked</link><guid isPermaLink="false">https://www.franklarosa.com/p/why-palmos-sucked</guid><dc:creator><![CDATA[Frank LaRosa]]></dc:creator><pubDate>Sun, 16 Mar 2008 05:02:00 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!Xq1Q!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b6b04ce-08ed-496a-ad30-12272b54c090_1170x1143.heic" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Xq1Q!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b6b04ce-08ed-496a-ad30-12272b54c090_1170x1143.heic" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Xq1Q!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b6b04ce-08ed-496a-ad30-12272b54c090_1170x1143.heic 424w, https://substackcdn.com/image/fetch/$s_!Xq1Q!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b6b04ce-08ed-496a-ad30-12272b54c090_1170x1143.heic 848w, https://substackcdn.com/image/fetch/$s_!Xq1Q!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b6b04ce-08ed-496a-ad30-12272b54c090_1170x1143.heic 1272w, https://substackcdn.com/image/fetch/$s_!Xq1Q!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b6b04ce-08ed-496a-ad30-12272b54c090_1170x1143.heic 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Xq1Q!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b6b04ce-08ed-496a-ad30-12272b54c090_1170x1143.heic" width="1170" height="1143" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3b6b04ce-08ed-496a-ad30-12272b54c090_1170x1143.heic&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1143,&quot;width&quot;:1170,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:226177,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/heic&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Xq1Q!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b6b04ce-08ed-496a-ad30-12272b54c090_1170x1143.heic 424w, https://substackcdn.com/image/fetch/$s_!Xq1Q!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b6b04ce-08ed-496a-ad30-12272b54c090_1170x1143.heic 848w, https://substackcdn.com/image/fetch/$s_!Xq1Q!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b6b04ce-08ed-496a-ad30-12272b54c090_1170x1143.heic 1272w, https://substackcdn.com/image/fetch/$s_!Xq1Q!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b6b04ce-08ed-496a-ad30-12272b54c090_1170x1143.heic 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">The author&#8217;s Treo 600 running &#8216;Inverti&#8217;, his Othello clone</figcaption></figure></div><p><em>Editor's note: This was originally published on March 16, 2008.</em></p><p>Someone showed me an early Palm Pilot. I was hooked immediately and bought my own a few days later.</p><p>In those days, a Palm Pilot had a lot of deficiencies. Memory was limited to 8MB, the screen was a paltry 160x160, and switching between programs was slow and cumbersome. But it was worth it for the novelty of having a touch screen computer small enough to fit in a pocket.</p><p>Some years later I heard about the Palm Treo, and picked up a Treo 600. Having a PDA and a phone together was the right idea, and the possibilities of an internet-connected Palm platform seemed endless. I installed the development tools with the intention of building a side career as a Palm applications developer.</p><p>The Treo 600 was fun for a little while, but it didn't take long for reality to sink in. The camera was too crude to take any kind of worthwhile picture. The screen, though color, was still only 160x160. Memory was only 24MB, and the applications were old and familiar &#8212; not in a warm way but in a way that makes you wonder what they'd been doing for the past five years.</p><p>Writing Palm applications was a shocking revelation. If you wrote Windows code in the Windows 3.1 days, you'd be right at home writing code for Palm OS because Palm is every bit as crippled and clunky as 16-bit Windows running on top of DOS. Cooperative multitasking, no memory protection, limited memory space. Dereference a nil pointer and crash the entire device.</p><p>I bought a new Treo in 2007, but it was only marginally better than the first one. The screen and the camera were a little better. The memory situation, the applications, and the programming tools were the same. The web browser stunk, third-party apps were terrible, and the whole thing was just too slow. Often it locked up or crashed when I was trying to do something basic like answer a call.</p><p>Watching Palm devices evolve was like watching the PC revolution in slow motion. What PCs gained in a year took the Palm people a decade.</p><p>In the meantime, Apple gave us a phone with a real computer in it &#8212; and a user interface that genuinely makes sense. When I used a Treo to browse the web, I expected it not to work at all; if I managed to get what I needed, I felt a deep sense of having pulled off something remarkable. With an iPhone it just works.</p><p>I hate being wrong, and I hate seeing so much work go down the drain, but it&#8217;s time to be honest: Palm blew it. With a ten-year head start, they could have owned the mobile market, but they squandered their advantage. Mobile is now Apple&#8217;s game, and while I may wax nostalgic for my Palm Pilots and Treos of the past, you can bet that Palm isn&#8217;t getting any more of my mindshare or my money.</p><p><em>I remain an enthusiastic iPhone user and iOS app developer to this day.</em></p>]]></content:encoded></item></channel></rss>