We're Making a Big Mistake

I believe that IT workers who are also passionate about gen AI are making a major misjudgment. We wrongly assume that the advances we observe in our field, such as the autonomous or semi-autonomous development of applications, also translate to sectors like medicine or law. This is a false generalization.

The field of IT heavily relies on strict formalism: the raw material consumed by LLMs. In the legal field, for example, this is not the case: it is much more complex. Laws, regulations, and judgments are generally written and presented in standardized forms, but the content is far from being as digestible formalism as lines of code written in a programming language. In my opinion, we should remember that when we share our enthusiasm for gen AI. We must be lucid while also setting the right expectations for decision-makers and lawmakers.

The Rise of Cognitive Dept

Margaret-Anne Storey introduces “cognitive debt” as a concept that may be more threatening than technical debt in AI-augmented development. Unlike technical debt (which lives in code), cognitive debt is the erosion of shared understanding that resides in developers’ minds. Drawing on Peter Naur’s concept of a program as a “theory” distributed across teams, the article argues that as AI and agentic tools push for development velocity, teams risk losing their collective understanding of why systems work the way they do. Even if AI generates technically clean code, teams can become paralyzed when no one can explain design decisions or anticipate the consequences of changes. The author calls for intentional slowdowns, collaborative practices, and serious research into measuring and mitigating this growing challenge.

“As generative and agentic AI accelerate development, protecting that shared theory of what the software does and how it can change may matter more for long-term software health than any single metric of speed or output.”

Traveling to Egypt and Jordan: Some of My Random Travel Notes

  • One of my favourite things while flying is seeing a plane go in the opposite direction at a distance, ideally close enough to feel the raw speed of the plane added to mine.

  • I loved Egypt, but the chaos of Cairo can be overwhelming and tiring. I prefer the relative calm of Jordan’s cities. I may be getting too old for some scenery.

  • WhatsApp is a dominant communication platform worldwide, except in North America. Too bad it is so awkward to navigate and is owned by Meta.

  • Taking pictures of the scene from my hublot is a good time killer and can produce fascinating images. I have so many of them.

  • Posted many pictures on my personal and custom-made photo-sharing website, and it worked beautifully. Of course, a selection of my photos was also posted on my Glass page. More to come in the next few weeks.

  • My personal custom-built RSS web app proved to be super handy when I wanted a quick overview of the news.

  • These notes were gathered and written using Raycast for iOS before being edited and published. Raycast > Tot, in my opinion.

  • I encountered several planes equipped with electronically activated polarization shields on the windows, which produce unusual lighting effects both outside and inside the cabin. Some shields do not completely block external light. Additionally, these shields are centrally controlled during flight, which can be frustrating.

  • Most airports remain and will always be customer-hostile and unfriendly. It seems to be getting worse every year, everywhere, as air travel continues to increase.

  • I brought my newly acquired Ubiquity Travel Router with me, but it was of no use; all hotel Wi-Fi, including the cruise ship, allowed many devices to connect with a single activation.

  • Taking a break from the intensity of the digital world is salutary. But I’m glad to be back online now.

I'm rebuilding Flickr!

Well, maybe not, but here’s a description of my recently created photo-sharing webapp. And I have many more ideas to improve this.

Photo Sharing WebApp - Feature Overview

A modern, full-stack travel photo gallery built with Next.js 15, featuring intelligent photo management, interactive maps, and seamless cloud storage integration.


🌟 Highlights

  • Zero-Database Architecture: Uses Vercel Blob for photos and Redis (via Vercel KV) for metadata
  • Privacy Controls: Public, unlisted, and private album visibility options
  • Interactive World Map: Displays photo locations extracted from EXIF GPS data
  • Responsive Design: Optimized for all devices from mobile to desktop
  • Admin Panel: Complete photo management without leaving the browser

Album Management

  • Collapsible Albums: Each travel album can be expanded or collapsed independently
  • Smart Defaults: Most recent album automatically expands on page load
  • Album Metadata: Title, description, and date for each collection
  • Privacy Levels:
    • Public: Visible to everyone on the homepage
    • Unlisted: Only accessible via direct link
    • Private: Visible only to authenticated admins

Photo Display

  • Grid Layout Options: Three display modes to suit your preference
    • Comfortable: Spacious 2-5 column grid with square thumbnails
    • Compact: Dense 3-6 column grid for maximum photos per screen
    • Masonry: Pinterest-style layout preserving original aspect ratios
  • Layout Persistence: Grid preference saved in browser localStorage
  • Newest First: Photos automatically sorted by upload date (newest at top-left)
  • Rounded Thumbnails: Modern, elegant aesthetic with subtle shadows
  • Hover Effects: Smooth scale and brightness animations on interaction
  • Upload Date Display: Shows when each photo was added (in comfortable/masonry modes)
  • Full-Screen Experience: Distraction-free photo viewing
  • Navigation Controls:
    • Keyboard arrows (← →) for previous/next
    • On-screen navigation buttons
    • ESC key to close
  • Photo Captions: Optional descriptions displayed below photos
  • Smooth Transitions: Animated photo changes with loading states
  • Mobile Optimized: Touch-friendly controls and responsive sizing

Interactive Features

  • World Map Integration:
    • Leaflet-based interactive map
    • Clustered markers for photos with GPS coordinates
    • Click markers to view photos from that location
    • Automatic bounds fitting to show all locations
    • Album information in marker popups
  • Random Featured Photo:
    • Displays a random photo from all albums on homepage
    • Changes on each page load
    • Shows caption if available
  • Mini Thumbnails:
    • Collapsed albums show preview of first 5 photos
    • Smooth animation on expand/collapse
    • Photo count badge for albums with 6+ photos

Content Syndication

  • RSS Feed: Subscribe to new photo uploads at /feed.xml
  • Analytics: Vercel Analytics integration for visitor tracking

🔐 Admin Panel Features

Authentication

  • Vercel Authentication: Secure login using your Vercel account
  • Protected Routes: /admin and sub-routes require authentication
  • Session Management: Logout functionality with redirect

Album Management

  • Create Albums:
    • Title (required)
    • Description (optional)
    • Date (optional, ISO format)
    • Privacy setting (public/unlisted/private)
  • Edit Albums:
    • Update all metadata including privacy settings
    • Inline editing interface
    • Cancel without saving
  • Delete Albums:
    • Confirmation dialog before deletion
    • Automatically deletes all photos in album
    • Removes both metadata and blob files

Photo Management

  • Bulk Upload:
    • Multi-file selection support
    • Drag-and-drop interface
    • Progress tracking for each file
    • Batch processing with individual status indicators
    • Total size calculation before upload
  • Upload Features:
    • Automatic EXIF extraction (GPS, camera, capture settings)
    • Optional captions (for single photo uploads)
    • Format support: JPG, PNG, WebP, and more
    • Visual upload progress bar
    • Success/error status for each file
  • Photo Organization:
    • Clickable photo count badges link to management page
    • Grid view of all photos in an album
    • Displays metadata: upload date, GPS, camera info
    • Empty state with call-to-action
  • Delete Photos:
    • Individual photo deletion from management page
    • Confirmation dialog with photo caption
    • Removes both Vercel Blob file and metadata
    • Instant UI update on successful deletion

Admin Dashboard

  • Album Overview:
    • List of all albums (sorted by date, newest first)
    • Photo count for each album
    • Privacy status indicators with icons
    • Quick access buttons (Edit, Upload, Manage, Delete)
  • Status Indicators:
    • 🌍 Public: Green badge with globe icon
    • 🔗 Unlisted: Yellow badge with link icon
    • 🔒 Private: Red badge with lock icon
  • Responsive Layout: Optimized for tablet and mobile management

🛠️ Technical Architecture

Frontend

  • Framework: Next.js 15 with App Router
  • Language: TypeScript for type safety
  • Styling: Tailwind CSS with custom configurations
  • Icons: Lucide React for consistent iconography
  • Image Optimization: Next.js Image component with automatic optimization
  • State Management: React hooks (useState, useEffect)
  • Client-Side Rendering: Dynamic imports for map components (SSR bypass)

Backend

  • API Routes: Next.js server-side API endpoints
  • File Upload: FormData with multipart handling
  • EXIF Parsing: exif-parser library for metadata extraction
  • Image Processing: Automatic format conversion and compression

Storage & Database

  • Photo Storage: Vercel Blob (CDN-backed object storage)
  • Metadata Storage: Redis via Vercel KV Marketplace integration
  • Database Client: ioredis for Redis connections
  • Data Structure:
    • Albums stored as JSON array in albums key
    • Photos stored per album in photos:{albumId} keys
    • No SQL database required

Deployment

  • Platform: Vercel
  • Environment:
    • BLOB_READ_WRITE_TOKEN: Vercel Blob access
    • KV_REDIS_URL: Redis connection string
  • CDN: Automatic edge caching for photos and pages
  • Domain: Custom domain support with automatic SSL

📊 Data Models

Album

{
  id: string;                          // album_timestamp_random
  title: string;                       // "Japan 2025"
  description: string;                 // Trip description
  date: string;                        // ISO date "2025-01-15"
  createdAt: number;                   // Unix timestamp
  visibility: "public" | "unlisted" | "private";
}

Photo

{
  id: string;                          // photo_timestamp_random
  albumId: string;                     // Reference to album
  blobUrl: string;                     // Vercel Blob URL
  caption?: string;                    // Optional description
  uploadedAt: number;                  // Unix timestamp
  metadata?: {
    gps?: {
      latitude: number;
      longitude: number;
      altitude?: number;
    };
    camera?: {
      make?: string;                   // "Canon"
      model?: string;                  // "EOS R5"
      lens?: string;
    };
    capture?: {
      dateTaken?: string;              // When photo was taken
      exposureTime?: string;           // "1/1000"
      fNumber?: number;                // 2.8
      iso?: number;                    // 100
      focalLength?: number;            // 50mm
    };
    dimensions?: {
      width: number;
      height: number;
    };
    orientation?: number;
  };
}

🎯 User Flows

Visitor Journey

  1. Land on homepage → See random featured photo
  2. View interactive world map with photo locations
  3. Browse albums (newest expanded by default)
  4. Click photo → Open lightbox viewer
  5. Navigate with arrows or keyboard
  6. Subscribe to RSS feed for updates

Admin Journey

  1. Navigate to /admin → Vercel auth redirect
  2. View dashboard with all albums and photo counts
  3. Create new album: Click “Create Album” → Fill form → Set privacy
  4. Upload photos: Click “Upload Photos” → Drag files → Add captions → Upload
  5. Manage photos: Click photo count badge → View grid → Delete unwanted photos
  6. Edit album: Click “Edit” → Update metadata/privacy → Save
  7. Delete album: Click “Delete” → Confirm → Album and all photos removed

🚀 Performance Optimizations

  • Image Optimization: Next.js automatic WebP conversion and lazy loading
  • CDN Caching: Vercel Edge Network caches photos globally
  • Code Splitting: Dynamic imports for heavy components (map)
  • Responsive Images: Multiple sizes served based on viewport
  • Minimal JavaScript: Client-side JS only where needed
  • Fast Page Loads: Static generation where possible
  • Efficient Queries: Single Redis calls for album/photo lists
  • LocalStorage: Grid layout preference cached client-side

🎨 Design Philosophy

Visual Design

  • Color Palette: Blue gradient accents with dark mode support
  • Typography: Clear hierarchy with responsive font sizes
  • Spacing: Generous whitespace for comfortable reading
  • Shadows: Layered depth with hover elevations
  • Animations: Subtle 300ms transitions throughout
  • Icons: Consistent Lucide icon library

User Experience

  • Progressive Disclosure: Collapsed albums reduce cognitive load
  • Keyboard Navigation: Full keyboard support in lightbox
  • Touch Optimization: Tap targets sized for mobile
  • Loading States: Spinners and skeletons during data fetches
  • Error Handling: User-friendly error messages
  • Confirmation Dialogs: Prevent accidental deletions

Accessibility

  • Semantic HTML: Proper heading hierarchy
  • Focus States: Visible keyboard focus indicators
  • Alt Text: Image descriptions for screen readers
  • Color Contrast: WCAG AA compliance
  • Responsive Design: Works on all screen sizes

📈 Use Cases

Personal Travel Blog

  • Document trips with organized photo albums
  • Share adventures with family and friends
  • Keep private memories secure with privacy controls

Photography Portfolio

  • Showcase work by location or project
  • Professional presentation with grid layouts
  • Metadata display for technical details

Family Photo Sharing

  • Create unlisted albums for family-only access
  • Easy upload from mobile devices
  • No complicated software required

Educational Projects

  • Demonstrate modern web development practices
  • Showcase Next.js 15 App Router patterns
  • Example of cloud storage integration

🔮 Future Enhancement Ideas

Photo Features

  • Bulk photo deletion (select multiple)
  • Photo reordering (drag-and-drop)
  • Photo editing (crop, rotate, filters)
  • Download full-resolution images
  • Print/photo book export

Search & Discovery

  • Full-text search across captions
  • Tag system for categorization
  • Timeline view (chronological)
  • Favorites/highlights collection

Social Features

  • Photo comments system
  • Social media share buttons
  • Individual photo permalinks
  • Public photo embeds

Analytics

  • View count tracking
  • Popular photos dashboard
  • Storage usage metrics
  • Visitor analytics

Mobile App

  • Native iOS/Android apps
  • Offline photo viewing (PWA)
  • Camera integration for uploads
  • Push notifications for new albums

💡 Why This Architecture?

Serverless-First

  • No server maintenance: Vercel handles infrastructure
  • Auto-scaling: Handles traffic spikes automatically
  • Global CDN: Photos served from edge locations
  • Cost-effective: Pay only for usage

Modern Stack

  • Type Safety: TypeScript catches bugs at compile time
  • React 19: Latest React features and optimizations
  • Next.js 15: App Router for improved performance
  • Tailwind CSS: Utility-first styling for rapid development

Cloud Storage

  • Vercel Blob: Purpose-built for media storage
  • Redis: Fast key-value storage for metadata
  • No database: Simpler architecture, fewer failure points
  • Atomic operations: Redis ensures data consistency

📝 Technical Decisions

Why ioredis instead of @vercel/kv?

The Vercel KV Marketplace integration provides a standard Redis URL, which works better with the ioredis package. This offers more flexibility and standard Redis features.

Why client-side rendering for the homepage?

The random featured photo and map interactions require JavaScript. Client-side rendering provides the most interactive experience while keeping the codebase simple.

Why separate manage page instead of edit-in-place?

Separating photo management from display keeps each view focused and performant. The upload page stays lightweight for quick uploads, while the manage page provides detailed controls.

Why not use a traditional database?

For a photo gallery, the access patterns are simple (list albums, list photos). Redis provides sub-millisecond reads and sufficient storage for metadata, while Vercel Blob handles the large files. This eliminates the need for PostgreSQL/MySQL.


🏗️ Project Structure

voyages-photo-gallery/
├── app/
│   ├── admin/
│   │   ├── manage/[albumId]/       # Photo management page
│   │   ├── upload/[albumId]/       # Photo upload page
│   │   └── page.tsx                # Admin dashboard
│   ├── api/
│   │   ├── admin/
│   │   │   ├── albums/
│   │   │   │   ├── [albumId]/      # Update/delete album
│   │   │   │   └── route.ts        # Create album
│   │   │   ├── photos/
│   │   │   │   └── route.ts        # Delete photo
│   │   │   └── upload/
│   │   │       └── route.ts        # Upload photos
│   │   ├── albums/
│   │   │   └── route.ts            # Get all albums (public)
│   │   ├── photos/
│   │   │   └── route.ts            # Get all photos (public)
│   │   └── auth/                   # Login/logout
│   ├── feed.xml/
│   │   └── route.ts                # RSS feed generation
│   ├── layout.tsx                  # Root layout with Analytics
│   ├── page.tsx                    # Public gallery homepage
│   └── globals.css                 # Global styles
├── components/
│   ├── AlbumSection.tsx            # Collapsible album component
│   ├── GridLayoutToggle.tsx        # Layout switcher
│   ├── Lightbox.tsx                # Photo viewer
│   └── WorldMap.tsx                # Interactive map
├── lib/
│   └── db.ts                       # Redis database functions
├── types/
│   └── index.ts                    # TypeScript type definitions
├── middleware.ts                   # Auth protection
└── package.json                    # Dependencies

🎓 Learning Resources

This project demonstrates:

  • Next.js 15 App Router patterns
  • TypeScript in React applications
  • Tailwind CSS utility-first styling
  • Vercel deployment and storage
  • Redis data modeling
  • EXIF metadata extraction
  • Responsive design principles
  • File upload handling
  • Authentication middleware
  • Dynamic routing
  • Client/server component separation

Perfect for developers learning modern full-stack web development!


📄 License

MIT License - Feel free to use this as a learning resource or starting point for your own projects.


🤝 Contributing

Built with Claude Code - AI-assisted development for rapid prototyping and feature implementation.

Technologies: Next.js 15 • React 19 • TypeScript • Tailwind CSS • Vercel • Redis • Leaflet

Built by: AI enthusiasts exploring the intersection of modern web development and AI-assisted coding

Building A Dedicated Photo-Sharing Website in Claude Code

Thinking about the upcoming trip to Egypt, I realized I still didn’t have a good solution for sharing photos and comments beyond the usual social networks. Drawing on my experience from the past few weeks deploying web applications on Vercel, I decided to try the same by building a website for sharing and viewing photos. The additional complexity here is that the viewing portion is separate from the photo upload section. Therefore, I need to protect this feature with a password. Additionally, image storage must be optimized to minimize costs and provide a pleasant, flexible viewing experience. I’m using Vercel-only blog storage and Redis for metadata store.

In less than 2 hours, I built a fully functional application with Claude Code and Vercel. Impressive.

Apple Creator Studio - A Few Comments

On today’s announcement by Apple:

  1. Now we know why it took so long for Apple to update Pages, Numbers and Keynote. Where is iWork? Why no iWork subscription without the pro stuff?
  2. I don’t understand this bundling of pro apps with consumer-generalistic apps. Is Apple trying to upsell Pro Apps to consumers via a new subscription? They might be. I don’t think pros want to get Numbers or Pages, though.
  3. Icons are utterly un-Apple, or Apple has become something I no longer relate to software-design-wise. It’s not a good sign.
  4. Yes, Apple does look like Adobe and … Microsoft. And less, Apple.
  5. No mention of Photomator. I believe merging Photomator into Photos is not a good idea; combining a photo browsing app with a photo editing app isn’t ideal. Currently, Photos functions more as a photo browser than as a dedicated editing tool, and both aspects risk becoming mediocre. I want a focused, serious photo-editing app that doesn’t try to rival Photoshop. Is that too much to ask? The challenge lies in Apple’s prioritization of profit over user needs, which makes it difficult to develop such an app.

What a strange start to the year.

On Scrollbars

After reading a recent Gruber article about the macOS Tahoe window-resizing issue, I found a setting in Appearance that keeps scrollbars always visible. It’s somewhat odd because of the scrollbar’s thickness. I wish Apple would make them thinner and less noticeable. I’m unsure if I’ll get used to this.

Note: On Windows 11, scrollbars are always visible by default but are less obtrusive. 🤷🏻‍♂️ Apple was once known for leading with excellent design and great visual taste, but this is less obvious nowadays.

Auto-generated description: A computer interface displays settings for appearance customization, including options for Liquid Glass, theme colors, and icon styles.

On Apple’s Deal with Google

Back in November, Google announced Private AI Compute, positioning themselves to offer something like Apple’s Private Cloud Compute. It might be something that OpenAI wasn’t willing to do or didn’t see a fit in their business mission. By offering Private AI Compute, Google might have secured the business with Apple. Anyway, it’s becoming impressive how Google is taking back the lead in AI. Lastly, maybe we will see Google Gemini being added to this week’s next beta of iOS 26.3 to go side-by-side with ChatGPT?

When Things Go Wrong With AI-Generated Code

My first bad experience: the code generated by Claude Code made my dashboard unresponsive in my browser. Eventually, the data stopped updating. After a ten-minute debugging session, I asked Claude Code to revert the change, and it did so promptly. But then I started getting execution failure notices on Discord. A lot of notifications. Then I started investigating…

It appears the browser was making frequent refresh requests to one of my workflows, which depleted my Claude pay-per-use credits. Bummber. Looking at my n8n dashboard, I saw that one of my workflows was failing because of that. Logs were confirming the problem with the interaction with Claude AI. As shown on the graphs below, my instance CPU usage went through the roof. Ouch. Now I know what happened, and the problem was fixed. Now, I should find a way to rate-limit this type of behaviour. That’s for tomorrow, I guess. 😅