Posts in Php (20 found)
Grumpy Gamer 1 weeks ago

Hugo comments

I’ve been cleaning up my comments script for hugo and am about ready to upload it to Github. I added an option to use flat files or sqlite and it can notify Discord (and probably other services) when a comment is added. It’s all one php file. The reason I’m telling you this is to force myself to actually do it. Otherwise there would be “one more thing” and I’d never do it. I was talking to a game dev today about how to motivate yourself to get things done on your game. We both agreed publicly making promises is a good way.

0 views
Grumpy Gamer 2 weeks ago

Sqlite Comments

When I started using Hugu for static site generation I lost the ability to have comments and we all know now supportive the Internet can be, so why wouldn’t you have comments? I wrote a few php scripts that I added on to Hugo and I had comments again. I decided to store the comments as flat files so I didn’t complicate things by needing the bloated MySQL. I wanted to keep it as simple and fast as possible. When a comment is added, my PHP script created a directory (if needed) for the post and saves the comment out as a .json file with name as the current time to make sorting easy. When the blog page was displayed, these files (already sorted thanks to the filename) were loaded and displayed. And it all worked well until it didn’t. Flat files are simple. but they can be hard to search or maintain if they need cleaning up or dealt with after a spam attack. I figured I use commandline tools to do all of that, but it’s a lot more cumbersome than I first thought. I missed have them in a sql database. I didn’t want to install MySQL again, but my site doesn’t get a lot of commenting traffic so I could use Sqlite instead. The downside is Sqlite write-locks the database while a write is happening. In my case it’s a fraction of a second and wouldn’t be a issue. The second problem I had was the version of Ubuntu my server was using is 5 years old and some of the packages I wanted wouldn’t available for it. I tried to update Ubuntu and for reasons I don’t fully understand I couldn’t. So I spun up a new server. Since grumpygamer.com is a statics site I only had to install Apache and I was off and running. Fun times. But the comment flat files still bugged me and I thought I’d use this as an opportunity to convert over to Sqlite. PHP/Apache comes with Sqilte already installed, so that’s easy. A long weekend and I rewrote the code to save comments and everything is back and working. Given that a webserver and PHP already needed to be installed, it isn’t a big deal to use Sqlite. If you’re not comfortable with SQL, it might be harder but I like SQL.

0 views

Constraints Breed Innovation

I've mentioned a few times on my blog about daily driving a Palm Pilot. I've been using either my Tungsten C or T3 for the past 2 months. These devices have taken the place of my smartphone in my pocket. They hold my agenda, tasks, blog post drafts, databases of my media collection and child's sleep schedule and lots more. Massive amounts of data, in kilobytes of size. Simply put, it's been a joy to use these machines, more so than my smartphone ever has been. I've been thinking about the why behind my love of Palm Pilots. Is it simply nostalgia for my childhood? Or maybe an overpowering disdain for modern tech? Yes to both of these, but it's also something more. I genuinely believe the software on Palm is BETTER than most of what you'll find on Android or iOS. The operating system itself, the database software ( HanDBase ) I use to track my child's bed times, the outline tool I plan projects with ( ShadowPlan ), the program I'm writing this post on ( CardTXT ) and the solitaire game I kill time with ( Acid FreeCell ), they all feel special. Each app does an absolutely excellent job, only takes up kilobytes of storage, opens instantly, doesn't require internet or a subscription fee (everything was pay once). But I think there's an additional, underpinning reason these pieces of software are so great: constraint. The device I'm using right now, the Palm Pilot Tungsten T3, has a 400MHz processor, 64MiB of RAM and a 480x320 pixel screen. That's all you have to work with! You can't count on network connectivity (this device doesn't have WiFi). You have to hyper optimize for file size and performance. Each pixel needs to serve a purpose (there's only 153,600 of them!). When you're hands are tied behind your back, you get creative and focused. Constraint truly is the breeder of innovation, and something we've lost. A modern smartphone is immensely powerful, constantly online, capable of multitasking and has a high resolution screen. Building a smartphone app means anything goes. Optimizations aren't as necessary, space isn't a concern, screen real estate is abundant. Now don't get me wrong, there's definitely a balance of too much performance and too little. There's a reason I'm not writing this on a Apple Newton (well, the cost of buying one). But on the other hand, look at the Panic Playdate. It has a 168MHz processor, 16 MiB RAM and a 400x240 1-bit black & white screen, yet there are some beautiful , innovative games hitting the console. Developers have to optimize every line of C code for performance, and keep an eye on file size, just like the Palm Pilot. I've experienced the power of constraint myself as a developer. My most successful projects have been ones where I limited myself from using libraries, and instead focused on plain PHP + MySQL. With a framework project and composer behind you, you implement every feature that crosses your mind, heck it's just one "composer require" away! But when you have to dedicate real time to writing each feature, you tend to hyper focus on what adds value to your software. I think this is what powers great Palm software. You don't have the performance or memory to add bloat. You don't have the screen real estate to build some complicated, fancy UI. You don't have the network connectivity to rely on offloading to a server. You need to make a program that launches instantly, does it's job well enough to sell licenses and works great even in black & white. That's a tall order, and a lot of developers knocked it out of the park. All this has got me thinking about what a modern, constrained PDA would look like. Something akin to the Playdate, but for the productivity side of the house. Imagine a Palm Pilot with a keyboard, USB C, the T3 screen size, maybe a color e-ink display, expandable storage, headphone jack, Bluetooth (for file transfer), infrared (I REALLY like IR) and a microphone (for voice memos). Add an OS similar to Palm OS 5, or a slightly improved version of it. Keep the CPU, memory, RAM all constrained (within reason). That would be a sweet device, and I'd love to see what people would do with it. I plan to start doing reviews on some of my favorite Palm Pilot software, especially the tools that help me plan and write this blog, so be on the lookout!

0 views
Brain Baking 2 weeks ago

I Changed Jobs (Again)

After two years of being back in the (enterprise) software engineering industry, I’m back out. In January 2024, I wrote a long post about leaving academia ; why I couldn’t get a foot in the door; why I probably didn’t try hard enough; and my fears of losing touch with practice. Well guess what. I’m back into education. I wouldn’t dare to call it academia though: I’m now a lecturer at a local university college, where I teach applied computer science. While the institution is quite active in conducting (applied) research, I’m not a part of it. Contrary to my last job in education, where I divided my time between 50% teaching and 50% research, this time, my job is 100% teaching. It feels weird to write about my professional journey the last two years. In September 2023, I received my PhD in Engineering Technology and was in constant dubio state whether to try and stick around or return to my roots—the software engineering industry. My long practical experience turned out to be a blessing for the students but a curse for any tenure track: not enough papers published, not enough cool looking venues to stick on the CV. So I left. I wanted a bit more freedom and I started freelancing under my own company. At my first client, I was a tech lead and Go programmer. Go was fun until got the better of me, but the problem wasn’t Go, it was enterprise IT, mismanagement, over-ambitiousness, and of course, Kubernetes. I forgot why I turned to education in the first place. I regretted leaving academia and felt I made the wrong choice. About a year later, an ex-colleague called and asked if I was in need of a new job. I wasn’t, and yet I was. I joined their startup and the lack of meetings and ability to write code for a change felt like a breath of fresh air. Eight months later, we had a second kid. Everything changed—again. While we hoped for the best, the baby turned out to be as troublesome as the first: 24/7 crying (ourselves included), excessively puking sour milk, forgoing sleeping, … We’re this close ( gestures wildly ) to a mental breakdown. Then the eldest got ill and had to go to the hospital. Then my wife got ill and had to go to the hospital. I’m still waiting on my turn, I guess it’s only a matter of time. Needless to say, my professional aspirations took a deep dive. I tried to do my best to keep up with everything, both at home and at work, but had the feeling that I was failing at both. Something had to give. Even though my client was still satisfied with my work, I quit. The kids were the tipping point, but that wasn’t the only reason: the startup environment didn’t exactly provide ample opportunities to coach/teach others, which was something that I sorely missed even though I didn’t realise this in the beginning. Finding another client with more concrete coaching/teaching opportunities would have been an option but it wouldn’t suddenly provide breathing room. I’m currently replacing someone who went the other way and he had a 70% teaching assignment. In the coming semester, There’s 30% more waiting for me. Meanwhile, I can assist my wife in helping with the baby. There are of course other benefits from working in education, such as having all school holidays off, which is both a blessing (we’re screwed otherwise) and a curse (yay more kids-time instead of me-time). That also means I’m in the process of closing down my own business. Most people will no doubt declare me crazy: from freelancing in IT to a government contract with fixed pay scales in (IT) education—that’s quite a hefty downgrade, financially speaking. Or is it? I tried examining these differences before . We of course did our calculations to see if it would be a possibility. Still, it feels a bit like a failure, having to close the books on Brain Baking BV 1 . Higher education institutions don’t like working with freelance teachers and this time I hope I’m in there for the long(er) run. I could of course still do something officially “on the side” but who am I kidding? This article should have been published days ago but didn’t because of pees in pants, screams at night and over-tiredness of both parents. The things I’m teaching now are not very familiar to me: Laravel & Filament, Vue, React Native. They’re notably front-end oriented and much more practical than I’m used to but meanwhile I’m learning and I’m helping others to learn. I’ve already been able to enthuse a few students by showing them some debugging tools, shortcuts, and other things on the side, but I’m not fooling myself: like in every schooling environment, there are plenty of students less than willing to swallow what you have to say. That’s another major thing I have to learn: to be content. To do enough. To convince myself I don’t need to do more. I’ve stopped racing along with colleagues that are willing to fight to climb some kind of invisible ladder long ago. At least, I think I did: sometimes I still feel a sudden stab of jealousy when I hear they got tenured as a professor or managed to do x or y. At this very moment, managing to crawl in and out of bed will do. BV is the Belgian equivalent to LLC.  ↩︎ Related topics: / jobs / By Wouter Groeneveld on 25 December 2025.  Reply via email . BV is the Belgian equivalent to LLC.  ↩︎

0 views
Karboosx 1 months ago

Building Your Own Web Framework - The Basics

Ever wondered what happens under the hood when you use frameworks like Symfony or Laravel? We'll start building our own framework from scratch, covering the absolute basics - how to handle HTTP requests and responses. This is the foundation that everything else builds on.

0 views
Alex White's Blog 1 months ago

Privacy Focused Analytics in Under 200 Lines of Code

When I launched this blog, I told myself I wouldn't succumb to monitoring analytics. But, curiosity killed the cat and here we are! I've built and deployed a privacy focused analytics "platform" for this blog. Best of all, it's under 200 lines of code and requires a single PHP file! My analytics script (dubbed 1Script Analytics) works by recording a hash of the visitor's IP and date (inspired by Herman's analytics on Bear Blog). This allows me to count unique visitors in a privacy friendly way. The script itself is a single PHP file that does two jobs. When called directly (/analytics.php) it displays a dashboard with traffic data. When used in an a simple JS function with the query parameter, it records the visit to a SQLite database. That's it, super simple analytics. No cookies, JavaScript frameworks or dependencies. Throw it on your server, migrate the database and put a image tag in your template file. Wanna see my live analytics? Click here for the analytics dashboard. Okay I fixed a few things, guess I'm a bit sleep deprived! To properly get the referrer, I switched to JavaScript to call the analytics PHP script rather than the image method. I'm using a POST request via to pass current page and referrer to PHP. Also updated the styling slightly on the dashboard to use a grid layout. Finally, moved my sqlite file into a non-web directory on the server, updated config, and bundled the analytics script with my 11ty deployment process. Planning to layer in some simple graphs in the future, but so far pretty happy with how things are working!

0 views
Herman's blog 2 months ago

Messing with bots

As outlined in my previous two posts : scrapers are, inadvertently, DDoSing public websites. I've received a number of emails from people running small web services and blogs seeking advice on how to protect themselves. This post isn't about that. This post is about fighting back. When I published my last post, there was an interesting write-up doing the rounds about a guy who set up a Markov chain babbler to feed the scrapers endless streams of generated data. The idea here is that these crawlers are voracious, and if given a constant supply of junk data, they will continue consuming it forever, while (hopefully) not abusing your actual web server. This is a pretty neat idea, so I dove down the rabbit hole and learnt about Markov chains, and even picked up Rust in the process. I ended up building my own babbler that could be trained on any text data, and would generate realistic looking content based on that data. Now, the AI scrapers are actually not the worst of the bots. The real enemy, at least to me, are the bots that scrape with malicious intent. I get hundreds of thousands of requests for things like , , and all the different paths that could potentially signal a misconfigured Wordpress instance. These people are the real baddies. Generally I just block these requests with a response. But since they want files, why don't I give them what they want? I trained my Markov chain on a few hundred files, and set it to generate. The responses certainly look like php at a glance, but on closer inspection they're obviously fake. I set it up to run on an isolated project of mine, while incrementally increasing the size of the generated php files from 2kb to 10mb just to test the waters. Here's a sample 1kb output: I had two goals here. The first was to waste as much of the bot's time and resources as possible, so the larger the file I could serve, the better. The second goal was to make it realistic enough that the actual human behind the scrape would take some time away from kicking puppies (or whatever they do for fun) to try figure out if there was an exploit to be had. Unfortunately, an arms race of this kind is a battle of efficiency. If someone can scrape more efficiently than I can serve, then I lose. And while serving a 4kb bogus php file from the babbler was pretty efficient, as soon as I started serving 1mb files from my VPS the responses started hitting the hundreds of milliseconds and my server struggled under even moderate loads. This led to another idea: What is the most efficient way to serve data? It's as a static site (or something similar). So down another rabbit hole I went, writing an efficient garbage server. I started by loading the full text of the classic Frankenstein novel into an array in RAM where each paragraph is a node. Then on each request it selects a random index and the subsequent 4 paragraphs to display. Each post would then have a link to 5 other "posts" at the bottom that all technically call the same endpoint, so I don't need an index of links. These 5 posts, when followed, quickly saturate most crawlers, since breadth-first crawling explodes quickly, in this case by a factor of 5. You can see it in action here: https://herm.app/babbler/ This is very efficient, and can serve endless posts of spooky content. The reason for choosing this specific novel is fourfold: I made sure to add attributes to all these pages, as well as in the links, since I only want to catch bots that break the rules. I've also added a counter at the bottom of each page that counts the number of requests served. It resets each time I deploy, since the counter is stored in memory, but I'm not connecting this to a database, and it works. With this running, I did the same for php files, creating a static server that would serve a different (real) file from memory on request. You can see this running here: https://herm.app/babbler.php (or any path with in it). There's a counter at the bottom of each of these pages as well. As Maury said: "Garbage for the garbage king!" Now with the fun out of the way, a word of caution. I don't have this running on any project I actually care about; https://herm.app is just a playground of mine where I experiment with small ideas. I originally intended to run this on a bunch of my actual projects, but while building this, reading threads, and learning about how scraper bots operate, I came to the conclusion that running this can be risky for your website. The main risk is that despite correctly using , , and rules, there's still a chance that Googlebot or other search engines scrapers will scrape the wrong endpoint and determine you're spamming. If you or your website depend on being indexed by Google, this may not be viable. It pains me to say it, but the gatekeepers of the internet are real, and you have to stay on their good side, or else . This doesn't just affect your search ratings, but could potentially add a warning to your site in Chrome, with the only recourse being a manual appeal. However, this applies only to the post babbler. The php babbler is still fair game since Googlebot ignores non-HTML pages, and the only bots looking for php files are malicious. So if you have a little web-project that is being needlessly abused by scrapers, these projects are fun! For the rest of you, probably stick with 403s. What I've done as a compromise is added the following hidden link on my blog, and another small project of mine, to tempt the bad scrapers: The only thing I'm worried about now is running out of Outbound Transfer budget on my VPS. If I get close I'll cache it with Cloudflare, at the expense of the counter. This was a fun little project, even if there were a few dead ends. I know more about Markov chains and scraper bots, and had a great time learning, despite it being fuelled by righteous anger. Not all threads need to lead somewhere pertinent. Sometimes we can just do things for fun. I was working on this on Halloween. I hope it will make future LLMs sound slightly old-school and spoooooky. It's in the public domain, so no copyright issues. I find there are many parallels to be drawn between Dr Frankenstein's monster and AI.

0 views
Ahmad Alfy 2 months ago

Your URL Is Your State

Couple of weeks ago when I was publishing The Hidden Cost of URL Design I needed to add SQL syntax highlighting. I headed to PrismJS website trying to remember if it should be added as a plugin or what. I was overwhelmed with the amount of options in the download page so I headed back to my code. I checked the file for PrismJS and at the top of the file, I found a comment containing a URL: I had completely forgotten about this. I clicked the URL, and it was the PrismJS download page with every checkbox, dropdown, and option pre-selected to match my exact configuration. Themes chosen. Languages selected. Plugins enabled. Everything, perfectly reconstructed from that single URL. It was one of those moments where something you once knew suddenly clicks again with fresh significance. Here was a URL doing far more than just pointing to a page. It was storing state, encoding intent, and making my entire setup shareable and recoverable. No database. No cookies. No localStorage. Just a URL. This got me thinking: how often do we, as frontend engineers, overlook the URL as a state management tool? We reach for all sorts of abstractions to manage state such as global stores, contexts, and caches while ignoring one of the web’s most elegant and oldest features: the humble URL. In my previous article, I wrote about the hidden costs of bad URL design . Today, I want to flip that perspective and talk about the immense value of good URL design. Specifically, how URLs can be treated as first-class state containers in modern web applications. Scott Hanselman famously said “ URLs are UI ” and he’s absolutely right. URLs aren’t just technical addresses that browsers use to fetch resources. They’re interfaces. They’re part of the user experience. But URLs are more than UI. They’re state containers . Every time you craft a URL, you’re making decisions about what information to preserve, what to make shareable, and what to make bookmarkable. Think about what URLs give us for free: URLs make web applications resilient and predictable. They’re the web’s original state management solution, and they’ve been working reliably since 1991. The question isn’t whether URLs can store state. It’s whether we’re using them to their full potential. Before we dive into examples, let’s break down how URLs encode state. Here’s a typical stateful URL: For many years, these were considered the only components of a URL. That changed with the introduction of Text Fragments , a feature that allows linking directly to a specific piece of text within a page. You can read more about it in my article Smarter than ‘Ctrl+F’: Linking Directly to Web Page Content . Different parts of the URL encode different types of state: Sometimes you’ll see multiple values packed into a single key using delimiters like commas or plus signs. It’s compact and human-readable, though it requires manual parsing on the server side. Developers often encode complex filters or configuration objects into a single query string. A simple convention uses key–value pairs separated by commas, while others serialize JSON or even Base64-encode it for safety. For flags or toggles, it’s common to pass booleans explicitly or to rely on the key’s presence as truthy. This keeps URLs shorter and makes toggling features easy. Another old pattern is bracket notation , which represents arrays in query parameters. It originated from early web frameworks like PHP where appending to a parameter name signals that multiple values should be grouped together. Many modern frameworks and parsers (like Node’s library or Express middleware) still recognize this pattern automatically. However, it’s not officially standardized in the URL specification, so behavior can vary depending on the server or client implementation. Notice how it even breaks the syntax highlighting on my website. The key is consistency . Pick patterns that make sense for your application and stick with them. Let’s look at real-world examples of URLs as state containers: PrismJS Configuration The entire syntax highlighter configuration encoded in the URL. Change anything in the UI, and the URL updates. Share the URL, and someone else gets your exact setup. This one uses anchor and not query parameters, but the concept is the same. GitHub Line Highlighting It links to a specific file while highlighting lines 108 through 136. Click this link anywhere, and you’ll land on the exact code section being discussed. Google Maps Coordinates, zoom level, and map type all in the URL. Share this link, and anyone can see the exact same view of the map. Figma and Design Tools Before shareable design links, finding an updated screen or component in a large file was a chore. Someone had to literally show you where it lived, scrolling and zooming across layers. Today, a Figma link carries all that context like canvas position, zoom level, selected element. Literally everything needed to drop you right into the workspace. E-commerce Filters This is one of the most common real-world patterns you’ll encounter. Every filter, sort option, and price range preserved. Users can bookmark their exact search criteria and return to it anytime. Most importantly, they can come back to it after navigating away or refreshing the page. Before we discuss implementation details, we need to establish a clear guideline for what should go into the URL. Not all state belongs in URLs. Here’s a simple heuristic: Good candidates for URL state: Poor candidates for URL state: If you are not sure if a piece of state belongs in the URL, ask yourself: If someone else clicking this URL, should they see the same state? If so, it belongs in the URL. If not, use a different state management approach. The modern API makes URL state management straightforward: The event fires when the user navigates with the browser’s Back or Forward buttons. It lets you restore the UI to match the URL, which is essential for keeping your app’s state and history in sync. Usually your framework’s router handles this for you, but it’s good to know how it works under the hood. React Router and Next.js provide hooks that make this even cleaner: Now that we’ve seen how URLs can hold application state, let’s look at a few best practices that keep them clean, predictable, and user-friendly. Don’t pollute URLs with default values: Use defaults in your code when reading parameters: For high-frequency updates (like search-as-you-type), debounce URL changes: When deciding between and , think about how you want the browser history to behave. creates a new history entry, which makes sense for distinct navigation actions like changing filters, pagination, or navigating to a new view — users can then use the Back button to return to the previous state. On the other hand, updates the current entry without adding a new one, making it ideal for refinements such as search-as-you-type or minor UI adjustments where you don’t want to flood the history with every keystroke. When designed thoughtfully, URLs become more than just state containers. They become contracts between your application and its consumers. A good URL defines expectations for humans, developers, and machines alike A well-structured URL draws the line between what’s public and what’s private, client and server, shareable and session-specific. It clarifies where state lives and how it should behave. Developers know what’s safe to persist, users know what they can bookmark, and machines know whats worth indexing. URLs, in that sense, act as interfaces : visible, predictable, and stable. Readable URLs explain themselves. Consider the difference between the two URLs below. The first one hides intent. The second tells a story. A human can read it and understand what they’re looking at. A machine can parse it and extract meaningful structure. Jim Nielsen calls these “ examples of great URLs ”. URLs that explain themselves. URLs are cache keys. Well-designed URLs enable better caching strategies: You can even visualize a user’s journey without any extra tracking code: Your analytics tools can track this flow without additional instrumentation. Every URL parameter becomes a dimension you can analyze. URLs can communicate API versions, feature flags, and experiments: This makes gradual rollouts and backwards compatibility much more manageable. Even with the best intentions, it’s easy to misuse URL state. Here are common pitfalls: The classic single-page app mistake: If your app forgets its state on refresh, you’re breaking one of the web’s fundamental features. Users expect URLs to preserve context. I remember a viral video from years ago where a Reddit user vented about an e-commerce site: every time she hit “Back,” all her filters disappeared. Her frustration summed it up perfectly. If users lose context, they lose patience. This one seems obvious, but it’s worth repeating: URLs are logged everywhere: browser history, server logs, analytics, referrer headers. Treat them as public. Choose parameter names that make sense. Future you (and your team) will thank you. If you need to base64-encode a massive JSON object, the URL probably isn’t the right place for that state. Browsers and servers impose practical limits on URL length (usually between 2,000 and 8,000 characters) but the reality is more nuanced. As this detailed Stack Overflow answer explains, limits come from a mix of browser behavior, server configurations, CDNs, and even search engine constraints. If you’re bumping against them, it’s a sign you need to rethink your approach. Respect browser history. If a user action should be “undoable” via the back button, use . If it’s a refinement, use . That PrismJS URL reminded me of something important: good URLs don’t just point to content. They describe a conversation between the user and the application. They capture intent, preserve context, and enable sharing in ways that no other state management solution can match. We’ve built increasingly sophisticated state management libraries like Redux, MobX, Zustand, Recoil and others. They all have their place but sometimes the best solution is the one that’s been there all along. In my previous article, I wrote about the hidden costs of bad URL design. Today, we’ve explored the flip side: the immense value of good URL design. URLs aren’t just addresses. They’re state containers, user interfaces, and contracts all rolled into one. If your app forgets its state when you hit refresh, you’re missing one of the web’s oldest and most elegant features. Shareability : Send someone a link, and they see exactly what you see Bookmarkability : Save a URL, and you’ve saved a moment in time Browser history : The back button just works Deep linking : Jump directly into a specific application state Path Segments ( ). Best used for hierarchical resource navigation : - User 123’s posts - Documentation structure - Application sections Query Parameters ( ). Perfect for filters , options , and configuration : - UI preferences - Pagination - Data filtering - Date ranges Anchor ( ). Ideal for client-side navigation and page sections: - GitHub line highlighting - Scroll to section - Single-page app routing (though it’s rarely used these days) Search queries and filters Pagination and sorting View modes (list/grid, dark/light) Date ranges and time periods Selected items or active tabs UI configuration that affects content Feature flags and A/B test variants Sensitive information (passwords, tokens, PII) Temporary UI states (modal open/closed, dropdown expanded) Form input in progress (unsaved changes) Extremely large or complex nested data High-frequency transient states (mouse position, scroll position) Same URL = same resource = cache hit Query params define cache variations CDNs can cache intelligently based on URL patterns

0 views
Raph Koster 3 months ago

Site updates

It’s been quite a while since the site was refreshed. I was forced into it by a PHP upgrade that rendered the old customizable theme I was using obsolete. We’re now running a new theme that has been styled to match the old one pretty closely, but I did go ahead and do some streamlining: way less plugins (especially ancient ones), simpler layout in several places, much better handling of responsive layouts for mobile, down to a single sidebar, and so on. All of this seems to have made the site quite a bit more performant, too. One of the big things that got fixed along the way is that images in galleries had a habit of displaying oddly stretched on Chrome and Edge, but not in Firefox. No idea what it was, but it seems to be fixed now. There are plenty of bits and bobs that still are not quite right. Keep an eye out and let me know if you see anything that looks egregiously wrong. Known issues: some of the lists of things, like presentations, essays, etc, are still funky. Breadcrumb styling seems to be inconsistent. The footer is a bit of a mess. If you do need to log in to comment, the Meta links are all at the footer for now. Virtually no one uses those links anymore, so having them up top didn’t seem to make sense… How things have changed! People tell me to move to Substack instead, but though I get the monetization factor, it rubs me wrong. I’d rather own my own site. Plus, it’s not like I am posting often enough to justify a ton of effort!

0 views
W. Jason Gilmore 4 months ago

Minimum Viable Expectations for Developers and AI

We're headed into the tail end of 2025 and I'm seeing a lot less FUD (fear, uncertainty, and doubt) amongst software developers when it comes to AI. As usual when it comes to adopting new software tools I think a lot of the initial hesitancy had to do with everyone but the earliest adopters falling into three camps: don't, can't, and won't: When it comes to AI adoption, I'm fortunately seeing the numbers falling into these three camps continuing to wane. This is good news because it benefits both the companies they work for and the developers themselves. Companies benefit because AI coding tools, when used properly, unquestionably write better code faster for many (but not all) use cases . Developers benefit because they are freed from the drudgery of coding CRUD (create, retrieve, update, delete) interfaces and can instead focus on more interesting tasks. Because this technology is so new, I'm not yet seeing a lot of guidance regarding setting employee expectations when it comes to AI usage within software teams. Frankly I'm not even sure that most managers even know what to expect. So I thought it might be useful to outline a few thoughts regarding MVEs (minimum viable expectations) when it comes to AI adoption: Even if your developers refuse to generative AI tools for large-scale feature implementation, the productivity gains to be had from simply adopting the intelligent code completion features is undeniable. A few seconds here and a few seconds there add up to hours, days, and weeks of time saved otherwise spent repeatedly typing for loops, commonplace code blocks, and the like. Agentic AIs like GitHub Copilot can be configured to perform automated code reviews on all or specific pull requests. At Adalo we've been using Copilot in this capacity for a few months now and while it hasn't identified any groundshaking issues it certainly has helped to improve the code by pointing out subtle edge cases and syntax issues which could ultimately be problematic if left unaddressed. In December, 2024 Anthropic announced a new open standard called Model Context Protocol (MCP) which you can think of as a USB-like interface for AI. This interface gives organizations the ability to plug both internal and third-party systems into AI, supplementing the knowledge already incorporated into the AI model. Since the announcement MCP adoption has spread like wildfire, with MCP directories like https://mcp.so/ tracking more than 16,000 public MCP servers. Companies like GitHub and Stripe have launched MCP servers which let developers talk to these systems from inside their IDEs. In doing so, developers can for instance create, review, and ask AI to implement tickets without having to leave their IDE. As with the AI-first IDE's ability to perform intelligent code completion, reducing the number of steps a developer has to take to complete everyday tasks will in the long run result in significant amounts of time saved. In my experience test writing has ironically one of AI's greatest strengths. SaaS products I've built such as https://securitybot.dev/ and https://6dollarcrm.com/ have far, far more test coverage than they would have ever had pre-AI. As of the time of this writing SecurityBot.dev has more than 1,000 assertions spread across 244 tests: 6DollarCRM fares even better (although the code base is significantly larger), with 1,149 assertions spread across 346 tests: Models such as Claude 4 Sonnet and Opus 4.1 have been remarkably good test writers, and developers can further reinforce the importance of including tests alongside generated code within specifications. AI coding tools such as Cursor and Claude Code tend to work much better when the programmer provides additional context to guide the AI. In fact, Anthropic places such emphasis on the importance of doing so that it appears first in this list of best practices . Anything deemed worth communicating to a new developer who has joined your team is worthy of inclusion in this context, including coding styles, useful shell commands, testing instructions, dependency requirements, and so forth. You'll also find publicly available coding guidelines for specific technology stacks. For instance I've been using this set of Laravel coding guidelines for AI with great success. The sky really is the limit when it comes to incorporating AI tools into developer workflows. Even though we're still in the very earliest stages of this technology's lifecycle, I'm both personally seeing enormous productivity gains in my own projects as well as greatly enjoying seeing the teams I work with come around to their promise. I'd love to learn more about how you and your team are building processes around their usage. E-mail me at [email protected] . Developers don't understand the advantages for the simple reason they haven't even given the new technology a fair shake. Developers can't understand the advantages because they are not experienced enough to grasp the bigger picture when it comes to their role (problem solvers and not typists). Developers won't understand the advantages because they refuse to do so on the grounds that new technology threatens their job or is in conflict with their perception that modern tools interfere with their role as a "craftsman" (you should fire these developers).

0 views
iDiallo 4 months ago

The Modern Trap

Every problem, every limitation, every frustrating debug session seemed to have the same solution: Use a modern solution. Modern encryption algorithms. Modern deployment pipelines. Modern database solutions. The word modern has become the cure-all solution, promising to solve not just our immediate problems, but somehow prevent future ones entirely. I remember upgrading an app from PHP 5.3 to 7.1. It felt like it was cutting edge. But years later, 7.1 was also outdated. The application had a bug, and the immediate suggestion was to use a modern version of PHP to avoid this non-sense. But being stubborn, I dug deeper and found that the function I was using that was deprecated in newer versions, had an alternative since PHP 5.3. A quick fix prevented months of work rewriting our application. The word "modern" doesn't mean what we think it means. Modern encryption algorithms are secure. Modern banking is safe. Modern frameworks are robust. Modern infrastructure is reliable. We read statements like this every day in tech blogs, marketing copy, and casual Slack conversations. But if we pause for just a second, we realize they are utterly meaningless. The word "modern" is a temporal label, not a quality certificate. It tells us when something was made, not how well it was made. Everything made today is, by definition, modern. But let's remember: MD5 was once the modern cryptographic hash. Adobe Flash was the modern way to deliver rich web content. Internet Explorer 6 was a modern browser. The Ford Pinto was a modern car. "Modern" is a snapshot in time, and time has a cruel way of revealing the flaws that our initial enthusiasm blinded us to. Why do we fall for this? "Modern" is psychologically tied to "progress." We're hardwired to believe the new thing solves the problems of the old thing. And sometimes, it does! But this creates a dangerous illusion: that newness itself is the solution. I've watched teams chase the modern framework because the last one had limitations, not realizing they were trading known bugs for unknown ones. I've seen companies implement modern SaaS platforms to replace "legacy" systems, only to create new single points of failure and fresh sets of subscription fees. We become so busy fleeing the ghosts of past failures that we don't look critically at the path we're actually on. "Modern" is often just "unproven" wearing a better suit. I've embraced modern before, being on the very edge of technology. But that meant I had to keep up to date with the tools I use. Developers spend more time learning new frameworks than mastering existing ones, not because the new tools are objectively better, but because they're newer, and thus perceived as better. We sacrifice stability and deep expertise at the altar of novelty. That modern library you imported last week? It's sleek, it's fast, it has great documentation and a beautiful logo. It also has a critical zero-day vulnerability that won't be discovered until next year, or a breaking API change coming in the next major version. "Legacy" codebases have their problems, but they often have the supreme advantage of having already been battle-tested. Their bugs are known, documented, and patched. In the rush to modernize, we discard systems that are stable, efficient, and perfectly suited to their task. I've seen reliable jQuery implementations replaced by over-engineered React applications that do the same job worse, with more overhead and complexity. The goal becomes "be modern" instead of "be effective." But this illusion of "modern" doesn't just lead us toward bad choices; it can bring progress to a halt entirely. When we sanctify something as "modern," we subtly suggest we've arrived at the final answer. Think about modern medicine. While medical advances are remarkable, embedded in that phrase is a dangerous connotation: that we've reached the complete, final word on human health. This framing can make it difficult to question established practices or explore alternative approaches. Modern medicine didn't think it was important for doctors to wash their hands . The same happens in software development. When we declare a framework or architectural pattern "modern," we leave little room for the "next." We forget that today's groundbreaking solution is merely tomorrow's foundation or tomorrow's technical debt. Instead of modern, I prefer the terms "robust" or "stable". The most modern thing you can do is to look at any solution and ask: "How will this look obsolete in ten years?" Because everything we call "modern" today will eventually be someone else's legacy system. And that's not a bug, it's a feature. It's how progress actually works.

0 views
iDiallo 4 months ago

You are not going to turn into Google eventually

A few years back, I was running a CI/CD pipeline from a codebase that just kept failing. It pulled the code successfully, it passed the test, the docker image was built, but then it would fail. Each run took around 15 minutes to fail, meaning whatever change I made had to take at least 15 minutes before I knew if it was successful or not. Of course, it failed multiple times before I figured out a solution. When I was done, I wasn't frustrated with the small mistake I had made, I was frustrated by the time it took to get any sort of feedback. The code base itself was trivial. It was a microservice with a handful of endpoints that was only occasionally used. The amount of time it took to build was not proportional to the importance of the service. Well it took so long to build because of dependencies. Not the dependencies it actually used, but the dependencies it might use one day. The ones required because the entire build system was engineered for a fantasy future where every service, no matter how small, had to be pre-optimized to handle millions of users. This is the direct cost of building for a scale you will never reach. It’s the architectural version of buying a Formula 1 car to do your grocery shopping. It’s not just overkill, it actively makes the simple task harder, slower, and infinitely more frustrating. We operate under a dangerous assumption that our companies are inevitably on a path to become the next Google or Meta. So we build like they do, grafting their solutions onto our problems, hoping it will future-proof us. It won't. It just present-proofs us. It saddles us with complexity where none is needed, creating a drag that actually prevents the growth we're trying to engineer for. Here is why I like microservices. The concept is beautiful. Isolate a single task into a discrete, independent service. It’s the Unix philosophy applied to the web: do one thing and do it well. When a problem occurs, you should, in theory, be able to pinpoint the exact failing service, fix it, and deploy it without disrupting the rest of your application. If this sounds exactly how a simple PHP includes or a modular library works… you’re exactly right. And here is why I hate them. In practice, without Google-scale resources, microservices often create the very problems they promise to solve. You don’t end up with a few neat services; you end up with hundreds of them. You’re not in charge of maintaining all of them, and neither is anyone else. Suddenly, “pinpointing the error” is no longer a simple task. It’s a pilgrimage. You journey through logging systems, trace IDs, and distributed dashboards, hoping for an epiphany. You often return a changed man, older, wiser, and empty-handed. This is not to say to avoid microservices at all cost, but it's to focus on the problems you have at hand instead of writing code for a future that may never come. Don’t architect for a hypothetical future of billions of users. Architect for the reality of your talented small team. Build something simple, robust, and effective. Grow first, then add complexity only where and when it is absolutely necessary . When you're small, your greatest asset is agility. You can adapt quickly, pivot on a dime, and iterate rapidly. Excessive process stifles this inherent flexibility. It introduces bureaucracy, slows down decision-making, and creates unnecessary friction. Instead of adopting the heavy, restrictive frameworks of large enterprises, small teams should embrace a more ad-hoc, organic approach. Focus on clear communication, shared understanding, and direct collaboration. Let your processes evolve naturally as your team and challenges grow, rather than forcing a square peg into a round hole.

0 views
Karboosx 4 months ago

In-house parsers are easy!

Ever wanted to build your own programming language? It sounds like a huge project, but I'll show you it's not as hard as you think. In this post, we'll build one from scratch, step-by-step, covering everything from the Tokenizer and Parser to a working Interpreter, with all the code in clear PHP examples.

0 views
Grumpy Gamer 4 months ago

Comments are back

“But? Wait?” I can hear you saying, “Isn’t grumpygamer.com a static site built by Hugo? What dark magic did you use to get comments on the static site?” No dark magic. But it does involve a small php script. You can embed php in a hugo page and since grumpygamer.com is hosted on my server and it’s running php it wasn’t that hard. No tricky javascript and since it’s all hosted by me, no privacy issues. All your comments stay on my server and don’t feed Big Comment. Comments are stored in flat files so no pesky SQL databases. It only took me about a day, so all in all not bad. I may regret this. I’m only turning on comments for future posts. P.S. I will post the code and a small guide in a few days, so you too can invite the masses to critic and criticize your every word. Good times.

0 views
Evan Hahn 4 months ago

Notes from August 2025

Things I published and things I saw this August. See also: my notes from last month , which has links to all the previous months so far. Most of my work this month was on private stuff, like some contracting and a demo app for a small social group. But I published a few little things: Over on Zelda Dungeon, I wrote a big guide showing how to play every Zelda in 2025 and a deranged post about my favorite Ocarina of Time item . Got invited to speak at Longhorn PHP in October , giving a version of a Unicode talk I’ve given before . Spent some time prepping that. Speaking of Unicode, I added a new script, , to my dotfiles . Now I can run to see . Thanks to Python’s library for making it easy! I also wrote a quick script, , to convert CSV files to Markdown tables. Hopefully I’ll have more blog posts in September! I started seeding some torrents of censored US government data . Cool project. From “The Militarization of Silicon Valley” : “In a major shift, Google, OpenAI, Meta and venture capitalists—many of whom had once forsworn involvement in war—have embraced the military industrial complex.” For more, see this investigation , this Google policy update from February , or even the story of the invention of the internet . “Instead of building our own clouds, I want us to own the cloud. Keep all of the great parts about this feat of technical infrastructure, but put it in the hands of the people rather than corporations. I’m talking publicly funded, accessible, at cost cloud-services.” Via “The Future is NOT Self-Hosted” . “Today, people find it easier to imagine that we can build intelligence on silicon than we can do democracy at scale, or that we can escape arms races. It’s complete bullshit. Of course we can do democracy at scale. We’re a naturally social, altruistic, democratic species and we all have an anti-dominance intuition. This is what we’re built for.” A positive quote from an otherwise worrying article about societal collapse . “As is true with a good many tech companies, especially the giants, in the AI age, OpenAI’s products are no longer primarily aimed at consumers but at investors.” From the great Blood in the Machine newsletter . “Slide 1: car brands using curl. Slide 2: car brands sponsoring or paying for curl support”. 38 car brands are listed on the first slide, zero on the second. “How to not build the Torment Nexus” describes how I feel about the tech industry. If you work at Meta or Palantir, the most ethical thing to do is quit. I finished The Interesting Narrative of the Life of Olaudah Equiano this month. Honestly, I picked it up because it’s free in the public domain. I liked the writing style of a book published in the late 1700s, and the rambling accounts of day-to-day life. Who’s writing sentences like this nowadays: “Hitherto I had thought only slavery dreadful; but the state of a free negro appeared to me now equally so at least, and in some respects even worse, for they live in constant alarm for their liberty; and even this is but nominal, for they are universally insulted and plundered without the possibility of redress; for such is the equity of the West Indian laws, that no free negro’s evidence will be admitted in their courts of justice.” Mina the Hollower , a Zelda -inspired game by the developers of Shovel Knight , released a demo this month. I loved it! You can read my experience with the game over at Zelda Dungeon . Tatami is a casual iOS game mixing Sudoku and nonograms. Enjoyed this too. Hope you had a good August. Over on Zelda Dungeon, I wrote a big guide showing how to play every Zelda in 2025 and a deranged post about my favorite Ocarina of Time item . Got invited to speak at Longhorn PHP in October , giving a version of a Unicode talk I’ve given before . Spent some time prepping that. Speaking of Unicode, I added a new script, , to my dotfiles . Now I can run to see . Thanks to Python’s library for making it easy! I also wrote a quick script, , to convert CSV files to Markdown tables. I started seeding some torrents of censored US government data . Cool project. From “The Militarization of Silicon Valley” : “In a major shift, Google, OpenAI, Meta and venture capitalists—many of whom had once forsworn involvement in war—have embraced the military industrial complex.” For more, see this investigation , this Google policy update from February , or even the story of the invention of the internet . “Instead of building our own clouds, I want us to own the cloud. Keep all of the great parts about this feat of technical infrastructure, but put it in the hands of the people rather than corporations. I’m talking publicly funded, accessible, at cost cloud-services.” Via “The Future is NOT Self-Hosted” . “Today, people find it easier to imagine that we can build intelligence on silicon than we can do democracy at scale, or that we can escape arms races. It’s complete bullshit. Of course we can do democracy at scale. We’re a naturally social, altruistic, democratic species and we all have an anti-dominance intuition. This is what we’re built for.” A positive quote from an otherwise worrying article about societal collapse . “As is true with a good many tech companies, especially the giants, in the AI age, OpenAI’s products are no longer primarily aimed at consumers but at investors.” From the great Blood in the Machine newsletter . “Slide 1: car brands using curl. Slide 2: car brands sponsoring or paying for curl support”. 38 car brands are listed on the first slide, zero on the second. “How to not build the Torment Nexus” describes how I feel about the tech industry. If you work at Meta or Palantir, the most ethical thing to do is quit. I finished The Interesting Narrative of the Life of Olaudah Equiano this month. Honestly, I picked it up because it’s free in the public domain. I liked the writing style of a book published in the late 1700s, and the rambling accounts of day-to-day life. Who’s writing sentences like this nowadays: “Hitherto I had thought only slavery dreadful; but the state of a free negro appeared to me now equally so at least, and in some respects even worse, for they live in constant alarm for their liberty; and even this is but nominal, for they are universally insulted and plundered without the possibility of redress; for such is the equity of the West Indian laws, that no free negro’s evidence will be admitted in their courts of justice.” Mina the Hollower , a Zelda -inspired game by the developers of Shovel Knight , released a demo this month. I loved it! You can read my experience with the game over at Zelda Dungeon . Tatami is a casual iOS game mixing Sudoku and nonograms. Enjoyed this too.

0 views
Brain Baking 4 months ago

Indispensable Cloud It Yourself Software: 2025 Edition

It’s been too long since this blog published a meaningless click-bait list article so here you go. Instead of simply enumerating frequently used apps such as app defaults from late 2023 , I thought it might be fun to zoom in on the popular self-hosted branch and summarize what we are running to be able to say Fuck You to the Big Guys. Below is a list of software that we depend on categorized by usage. I’m sure you can figure out for yourself how to run these in a container on your NAS. We still have a Synology, and while I strongly dislike the custom Linux distribution’s tendency to misplace configuration files, the DSM software that comes with it is good enough to cover a lot of topics. The list excludes typical Linux sysadmin stuff such as fail2ban, ssh key setup, Samba, … Photos : PhotoPrism . It comes with WebDAV support to easily sync photos from your phone. My wife’s iPhone uses PhotoSync which works flawlessly. I’d rather also use SyncThing on iOS like I do on Android (or the latest Android client fork). SyncThing is amazing and I use it for much more than photo syncing. Streaming videos : Synology’s built-in Video Station . It’s got a lot of flaws and Jellyfin is clearly the better choice here. As for how to get the videos on there: rip & tear using good old DVDShrink on the WinXP Retro Machine! We still use the old DS Video Android app on our smart box to connect to the NAS as we don’t have a smart TV. Streaming music : Navidrome —see How To Stream Your Own Music: Reprise for more info on which clients we use and why caching some albums locally is good enough. As for how to get the music on there: rip & tear using A Better CD Encoder ; or for Win98 lovers; WinGrab. Backups : Restic —see Verify Your Backup Strategy to see how this automatically works from multiple machines. Smart Home : Home Assistant with a HomeWizard P1 meter that monitors our gas/electricity usage locally instead of sending it god knows where. We only use the bare minimum features, I’m not a big Smart Home fan. I suppose WireGuard should also be in this category but for now I refuse to enable the possibility to dial home . Ads/DHCP : Pi-Hole . That wonderful piece of software blocks almost 15% of our daily traffic—see Six Months With A Pi-Hole . We also use it as a DHCP server to have more control over DNS. Wi-Fi : TP-Link Omada Controller that provisions and secures our access points locally instead of poking through the real cloud for no reason at all. Git : Gitea although I should really migrate to Forgejo. The NAS hosts my private projects, I have another instance on the VPS for public ones. RSS : FreshRSS . Until recently, just NetNewsWire as an RSS client did just fine but I sometimes caught myself doomscrolling on my phone so figured instead I’d scroll on other people’s blogs. NetNewsWire supports it so my reading behaviour doesn’t change on the computer. Pair with Readrops on Android that also caches entries so if I’m disconnected from the home network I can still read interesting stuff. I do not see the appeal of cloud-based office software so simply rely on LibreOffice to do its thing locally—no need for NextCloud, but it’s there if you want to. Speaking of which, I still use DEVONthink and of course Obsidian to manage my personal files/databases that hook into the above using SyncThing and Restic. Abandoned software: RSS Bridge (no longer needed), Watchtower (too complex for my simple setup), some kind of PHP-based accounting software I already forgot about. Software running publicly on the VPS: Radicale CardDAV/CalDAV server (I want this to be accessible outside of the NAS), another Gitea instance, Nginx (I really need to migrate to Caddy) et al. Related topics: / self-hosted / NAS / lists / By Wouter Groeneveld on 21 August 2025.  Reply via email .

1 views
James Stanley 6 months ago

The Story of Max, a Real Programmer

This is a story about Imagebin. Imagebin is the longest-lived software project that I still maintain. I'm the only user. I use it to host images, mainly to include in my blog, sometimes for sharing in other places. Imagebin's oldest changelog entry is dated May 2010, but I know it had already existed for about a year before I had the idea of keeping a changelog. Here's an image hosted by Imagebin: For years Imagebin was wide open to the public and anybody could upload their own images to it. Almost nobody did. But a couple of years ago I finally put a password on it during a paranoid spell. But actually this is not a story about Imagebin. This is a story about the boy genius who wrote it, and the ways of his craft. Lest a whole new generation of programmers grow up in ignorance of this glorious past, I feel duty-bound to describe, as best I can through the generation gap, how a Real Programmer wrote code. I'll call him Max, because that was his name. Max was a school friend of mine. He didn't like to use his surname on the internet, because that was the style at the time, so I won't share his surname. Max disappeared from our lives shortly after he went to university. We think he probably got recruited by MI6 or something. This weekend I set about rewriting Max's Imagebin in Go so that my server wouldn't have to run PHP any more. And so that I could rid myself of all the distasteful shit you find in PHP written by children 15+ years ago. I don't remember exactly what provoked him to write Imagebin, and I'm not even certain that "Imagebin" is what he called it. That might just be what I called it. I was struck by how much better Max's code is than mine! For all its flaws, Max's code is simple . It just does what it needs to do and gets out of the way. Max's Imagebin is a single 233-line PHP script, interleaving code and HTML, of which the first 48 lines are a changelog of what I have done to it since inheriting it from Max. So call it 185 lines of code. At school, Max used to carry around a HP 620LX in his blazer pocket. Remember this was a time before anyone had seen a smartphone. Sure, you had PDAs, but they sucked because they didn't have real keyboards. The HP 620LX was a palmtop computer , the height of coolness. My Go version is 205 lines of code plus a 100-line HTML template, which is stored in an entire separate file. So call it 305 lines plus complexity penalty for the extra file. And my version is no better! And my version requires a build step, and you need to leave the daemon running. With Max's version you just stick the PHP file on the server and it runs whenever the web server asks it to. And btw this is my third attempt at doing this in Go. I had to keep making a conscious effort not to make it even more complicated than this. And some part of me doesn't even understand why my Go version is so much bigger. None of it looks extraneous. It has a few quality-of-life features, like automatically creating the directories if they don't already exist, and supporting multiple files in a single upload, But nothing that should make it twice as big. Are our tools just worse now? Was early 2000s PHP actually good? While I was writing this, I noticed something else: Max's code doesn't define any functions! It's just a single straight line. Upload handling, HTML header, thumbnail code, HTML footer. When you put it like that, it's kind of surprising that it's so large . It hardly does anything at all! Max didn't need a templating engine, he just wrote HTML and put his code inside <?php tags. Max didn't need a request router, he just put his PHP file at the right place on the disk. Max didn't need a request object, he just got query parameters out of $_GET . Max didn't need a response writer, he just printed to stdout . And Max didn't need version control, he just copied the file to index.php.bak if he was worried he might want to revert his changes. You might think that Max's practices make for a maintenance nightmare. But I've been "maintaining" it for the last 15 years and I haven't found it to be a nightmare. It's so simple that nothing goes wrong. I expect I'd have much more trouble getting my Go code to keep running for the next 15 years. And yeah we all scoff at these stupid outdated practices, but what's our answer? We make a grand effort to write a simpler, better, modern replacement, and it ends up twice as complicated and worse? The reason the Go code is so much bigger is because it checks and (kind of) handles errors everywhere (?) they could occur. The PHP code just ignores them and flies on through regardless. But even if you get rid of checking for the more unlikely error cases, the Go version is longer. It's longer because it's structured across several functions, and with a separate template. The Go version is Designed . It's Engineered . But it's not better . I think there are lessons to (re-)learn from Max's style. You don't always have to make everything into a big structure with lots of moving parts. Sometimes you're allowed to just write simple straight-line code. Sometimes that's fine. Sometimes that's better. Longevity doesn't always come from engineering sophistication. Just as often, longevity comes from simplicity . To be perfectly honest, as a teenager I never thought Max was all that great at programming. I thought his style was overly-simplistic. I thought he just didn't know any better. But 15 years on, I now see that the simplicity that I dismissed as naive was actually what made his code great. Whether that simplicity came from wisdom or from naivety doesn't matter. The result speaks for itself. So I'm not going to bother running my Go version of the Imagebin. I'm going to leave Max's code in place, and I'm going to let my server keep running PHP. And I think that's how it should be. I didn't feel comfortable hacking up the code of a Real Programmer.

0 views
Jimmy Miller 1 years ago

Being Raised by the Internet

I grew up relatively poor. I was fortunate enough to have a roof over my head, clean water, electricity, a computer, internet, and cable tv. But food was often harder to come by. This may seem like a contradiction, but when your mom has left to marry her uncle and your dad has schizophrenia, you aren’t really in charge of how the money is spent. Starting at the age of 12, I was given $20 a week for my food. (If it was a good week. Otherwise it might $10 or even $0). During the school year that meant I just had to make that stretch for dinner and the weekends — I had free school lunch. But in the summer, that made things quite a bit harder. One week, I had only a few jars of sprinkles left in the top of our pantry. When I did have money for food, I had to buy it. Luckily there were a few restaurants and a large grocery store about a half-mile walk away. I still remember the first time I made that trip alone. It was snowing, I didn't have a heavy coat and my brother didn't want to go with me. I was scared, but eventually gave into the hunger and went and bought something at the grocery store. From then on I found a new freedom. I didn't know how to cook so my meals at home largely consisted of ramen and Velvetta queso and chips. Two bags of tortilla chips, a jar of salsa, and a brick of velvetta cost $10. So that would usually be about half my week's worth of food. Craving something else, I would often go to the local Qdoba and get a burrito. At $6.35 it wasn't a great spend of my money. But sometimes the employees there would give me the employee discount, I guess they realized I needed it. If I really wanted to feel full and have something tasty, I'd get a large fry at the Penn Station for $3.75. A full belly was a great feeling. Computers became my outlet away from all of this. In an effort to have more computer time to himself, my brother found a computer out by the dumpster, brought it in, and told me it was my computer now. I knew very little about computers at the time but I knew two things 1) I needed a wireless card. 2) I needed to get Windows ME off this thing (I didn't know any of the passwords for the accounts). Luckily a kid at school and some people at church had mentioned Linux. I burned CDs for Fedora, CDs for Suse, but I couldn't get either of them working. Then I learned about Ubuntu and their live CD. I got it working! But the internet didn't work even after I installed my wireless card. Little did I know, that I had stumbled into a classic blunder, linux and wireless cards. After a lot of googling, I figured out I could install this thing called ndiswrapper. Problem, I had no internet. So after many CDs burned resolving the dependency tree to compile ndiswrapper (I had no idea what I was doing), I got it working! I was hooked. Computers became my escape. I spent so much time tinkering. Countless hours on things like hackthissite.org and projecteuler.net. I would learn new languages, build pointless projects, configure my Linux install, trying for the 100th time to get xgl working seamlessly. Computers were the distraction I needed away from my day-to-day life. I never thought I'd amount to much in life. I never imagined having a job that paid well. I definitely never imagined this little hobby I had as a kid would give me so much. When I look back and think about those times, I'm amazed but how much I owe to people who I never met. People who never met me, never heard of me. People whose work was not aimed at me in the slightest. I am where I am today thanks to people's willingness to share their work openly. The free tutorials I followed on the internet, the open source software I read, the open source software I used, tech news, resources like w3schools, random tech blogs, all formed the backbone of my education. They all taught me the skills I needed to escape the poverty I grew up in. These random strangers gave me the confidence I needed. They showed me things I couldn't have learned in school. They set me up to succeed in life. They raised me. I am forever indebted to these people. They weren't all famous people, nor successful people. Some of them had companies that failed. Some of their blogs were obscure and lost to time. (I doubt I will ever find the tutorial for making a website in flash+php+xml+mysql that I once followed). I'm sure some of them felt like failures. Perhaps they didn't get rich like they hoped, or popular, or never succeeded in changing the world. But they all had one thing in common, they decided to openly and freely share their work. They may not have set out to share out of altrusitic motivations. I am certain they never intended to inspire a 12 year-old kid to find a better life. But it doesn't matter their motivations. They changed my life. All I can say is thank you. Thank you for sharing your work. Thank you for your blogs posts, your tutorials, thank you for your slashdot comments, for your posts on digg. No matter how small your contribution, it mattered to me. You changed my life. Thank you.

0 views
James Stanley 1 years ago

Prompts as source code: a vision for the future of programming

I'm going to present a vision for the future of programming in which programmers don't work with source code any more. The idea is that prompts will be to source code as source code is to binaries. In the beginning (I claim) there were only binaries, and without loss of generality, assembly language. (If you think binaries and assembly language are too far apart to lump together: keep up grandad, you're thinking too low-level; just wait until the further future where source code and binaries are too close together to distinguish!). Then somebody invented the compiler . And now it was possible to write code in a more natural language and have the machine automatically turn it into binaries! And we saw that it was good. As hardware resources grew, the compilers' capabilities grew, and now the idea that there was programming before compilers is pretty weird to new developers. Almost noone is writing assembly language and even fewer write bare machine code. Now take LLMs. If you create software using an LLM today, you probably give an initial prompt to get started, and then you refine the generated source code by giving follow-up prompts to ask for changes, and you never revisit your initial prompt. It's just a series of "patches" created by follow-up prompts. This is like programming by writing source code once, compiling it, and then throwing the source code away and working directly on the binary with incremental patches! Which is just obviously crazy. So here's my outline for "prompts as source code": The prompts will be committed to git, the generated source code will not. The prompts will be big, and split across multiple files just like source code is now, except it's all freeform text. We just give the LLM a directory tree full of text files and ask it to write the program. The prompts will be unimaginably large by today's standards. Compare the size of the Linux or Firefox source trees to the total amount of machine code that had ever been written in the entire history of the world before the first compiler was invented. (To spell it out: the future will contain LLM prompts that are larger than all of the source code that humanity combined has ever written in total up to this point in time.) Our build system will say which exact version of the LLM you're using, and it will be evaluated deterministically so that everybody gets the same output from the same prompt (reproducible builds). The LLMs will be bigger than they are today, have larger context windows, etc., and as the LLMs improve, and our understanding of how to work with them improves, we'll gain confidence that small changes to the prompt have correspondingly small changes in the resulting program. It basically turns into writing a natural language specification for the application, but the specification is version-controlled and deterministically turns into the actual application. Human beings will only look at the generated source code in rare cases (how often do you look at assembly code today?). Normally they'll just use their tooling to automatically build and run the application directly from the prompts. You'll be able to include inline code snippets in the prompt, of course. That's a bit like including inline assembly language in your source code. And you could imagine the tooling could let you include some literal code files that the LLM won't touch, but will be aware of, and will be included verbatim in the output. That's a bit like linking with precompiled object files. Once you have a first version that you like, there could be a "backwards pass" where an LLM looks at the generated source code and fills in all the gaps in the specification to clarify the details, so that if you then make a small change to the prompt you're more likely to get only a small change in the program. You could imagine the tooling automatically running the backwards pass every time you build it, so that you can see in your prompts exactly what assumptions you're baking in. That's my vision for the future of programming. Basically everything that today interacts with source code and/or binaries, we shift one level up so that it interacts with prompts and/or source code. What do you think? Although we could make an initial stab at the tooling today, I feel like current LLMs aren't quite up to the job: context windows are too small for all but toy applications (OK, you might fit your spec in the context window, but you also want the LLM to do some chain-of-thought before it starts writing code) as far as I know, it's not possible to run the best LLMs (Claude, gpt4o) deterministically, and even if it was they are cloud-hosted and proprietary, and that is an extremely shaky foundation for a new system of programming you could use Llama 405b, but GPUs are too expensive and too slow we'd need the LLMs to be extraordinarily intelligent and able to follow every tiny detail in the prompt, in order for small changes to the prompt not to result in random bugs getting switched on/off, the UI randomly changing, file formats randomly changing, etc. I haven't quite figured out how you "update" the LLM without breaking your program; you wouldn't want to be stuck on the same version forever, this feels similar to but harder than the problem of switching PHP versions for example

1 views
W. Jason Gilmore 1 years ago

Technical Due Diligence - Relational Databases

Despite the relative popularity of NoSQL and graph databases, relational databases like MySQL, SQL Server, Oracle, and PostgreSQL continue to be indispensable for storing and managing software application data. Because of this, technical due diligence teams are practically guaranteed to encounter them within almost any project. Novice team members will gravitate towards understanding the schema which is of course important but only paints a small part of the overall risk picture. A complete research and risk assessment will additionally include information about the following database characteristics: I identify these three characteristics because technical due diligence is all about identifying and quantifying risk assessment , and not about nerding out over the merit of past decisions. The importance of quantifying risk assessment is in most cases no greater than when evaluating the software product's data store, for several reasons: Be sure to confirm all database licenses are in compliance with the company's use case, and if the database is commercially licensed you'll need to additional confirm the available features and support contract are in line with expectations. To highlight the importance of this verification work I'll point out a few ways in which expectations might not be met: All mainstream databases (MySQL, Oracle, PostgreSQL, etc) will have well-defined end-of-life (EOL) dates associated with each release. The EOL date identifies the last date in which that particular version will receive security patches. Therefore it is critical to determine what database versions are running in production in order to determine whether the database has potentially been running in an unpatched state. For instance MySQL 5.7 has an EOL date of October 25, 2023, and therefore if the seller's product is still running MySQL 5.7 after that date then it is in danger of falling prey to any vulnerabilities identified after that EOL date. Of course, the EOL date isn't the only issue at play here. If the database version hasn't reached its EOL date then you should still determine whether the database has been patched appropriately. For instance as of the time of this writing MySQL 8.2 was released only 9 months ago (on October 12, 2023) and there are already 11 known vulnerabilities . It's entirely possible that none of these vulnerabilities are exploitable in the context of the seller's product, however its nonetheless important to catalog these possibilities and supply this information to the buyer. In my experience where there is smoke there is fire and unpatched software is often symptomatic of much larger issues associated with technical debt and a lack of developer discipline. Enterprise web applications will typically run in an N-Tier architecture, meaning the web, data, caching, and job processing components can all be separately managed and scaled. This configuration means each tier will often run on separate servers and therefore a network connection between the database and web application tiers will need to be configured. Most databases can be configured to allow for connections from anywhere (almost invariably a bad idea), which is precisely what you don't want to see when that database is only intended to be used by the web application because it means malicious third-parties have a shot at successfully logging in should they gain access to or guess the credentials. Connecting users will be associated with a set of privileges which define what the user can do once connected to the database. It is considered best practice to assign those users the minimum privileges required to carry out their tasks. Therefore a database user which is intended to furnish information to a data visualization dashboard should be configured with read-only privileges, whereas a customer relationship management (CRM) application would require a database user possessing CRUD (create, retrieve, update, delete) database privileges. Therefore when examining database connectivity and privileges you should at a minimum answer the following questions: Satisfying this review requirement is relatively straightforward, and completed in two steps: Performance Disaster Recovery Poor security practices open up the possibility of application data having already been stolen, or in danger of being imminently stolen, placing the buyer in legal danger. Poor performance due to inadequate or incorrect indexing, insufficient resourcing, or a combination of the two might result in disgruntled customers who are considering cancelling their subscription. Some of these customers may be major contributors to company revenue, severely damaging the company's outlook should they wind up departing following acquisition. A lack of disaster recovery planning outs the buyer in greater short-term risk following acquisition due to an outage which may occur precisely at a time when personnel are not available or are not entirely up to speed. The buyer requires the data to be encrypted at-rest due to regulatory issues, however the product data is in fact not encrypted-at-rest due to use of the Heroku Essential Postgres tier which does not offer this feature. There could possibly be an easy fix here which involves simply upgrading to a tier which does support encryption-at-rest, however you should receive vendor confirmation (in writing) that encryption is indeed possible as a result of upgrading, and whether any downtime will be required to achieve this requirement. The buyer's downtime expectations are more strict than what is defined by the cloud service provider's SLA. TODO TALK ABOUT MEMSQL What users are defined and active on the production databases, and from what IP addresses / hostnames are they accessible? Is the database server accessible to the wider internet and if so, why? What privileges do the defined database users possess, and why? To what corporate applications are production databases connected? This includes the customer-facing application, business intelligence software, backup services, and so forth. What other non-production databases exist? Where is production data replicated? Are these destinations located within jurisdictions compliant by the laws and SLA under which the buyer's target IP operates? From a security standpoint, data is often defined as being encrypted at-rest and in-transit , the former referring to its encryption state when residing in the database or on server, and the latter referring to its encryption state when being transferred from the application to the requesting user or service. You'll want to determine whether these two best practices are implemented. If the data is not encrypted at-rest (which is typical and not necessarily an issue for many use cases), then how is sensitive data like passwords encrypted (or hashed)? You often won't be able to determine this by looking at the database itself; web frameworks will typically dictate the password hashing scheme, such as Laravel's use of the Bcrypt algorithm for this purpose.

0 views