megacolorboy

Abdush Shakoor's Weblog

Writings, experiments & ideas.

Resize images from a file list using ImageMagick

Today, a colleague of mine uploaded a large number of images, only to realise later that they were all uncompressed and the site was loading noticeably slower.

His first thought was to compress them and re-upload everything again—but why do something redundant when you can handle it easily from the terminal using ImageMagick?

Previously, I’ve written about how ImageMagick makes it easy to resize images in bulk. However, sometimes you don’t want to touch every image in a directory.

In cases like this, if you already know which images need resizing, you can list their filenames in a text file and let ImageMagick process only those.

Assume a files.txt like this:

image1.jpg
photo_02.png
banner.jpeg

You can then resize just those images while keeping the original aspect ratio intact and avoiding upscaling:

while IFS= read -r file; do
  [ -f "$file" ] && mogrify -resize 500x600\> "$file"
done < files.txt

This works well when cleaning up large media folders or fixing legacy content where only a subset of images needs adjustment.

Hope you found this tip useful!

Can AI really replace software engineers?

My take on the impact of AI in the near future for software engineers.

I’ve spoken about this topic on this blog two years ago. But with how dramatically the AI landscape has changed—especially with the advent of more advanced models—I think it’s worth revisiting.

Think about it: if companies like OpenAI, Anthropic, or Microsoft truly believed that AI could replace software engineers, why would they still aggressively hunt for top engineering talent in Silicon Valley or spend billions acquiring startups?

Task or Responsibility?

Here’s how I see it in this AI era: AI can replace many programming tasks, but not the role or responsibility itself.

Programming is only one part of the job. If you step back and think about what you actually do, you’ll realize there’s a lot more involved than just writing code in your favorite editor.

This is where many people go wrong—by conflating a task with the role. It’s similar to saying calculators replaced mathematicians or accountants. Yes, calculators automated arithmetic, but they also enabled people to focus on more complex problems. Arithmetic was never the job; understanding the principles behind it was.

AI works the same way. It makes execution faster, but it doesn’t replace understanding.

What AI can’t do?

Think about what you actually do in a typical week.

You sit in closed rooms with project managers and clients who describe vague or unintelligible problems. You’re the one who decodes what they actually need. You look at the codebase and:

  • Figure out which parts need to change and which must remain untouched
  • Push back on feature requests that might introduce long-term technical debt
  • Review a colleague’s code before it reaches production
  • Decide whether something is ready to go live or needs more testing

There are many more responsibilities like this—and none of them are programming.

It’s just your job.

Raising concerns

This post isn’t meant to turn a blind eye to what’s happening in the industry.

We’ve seen massive layoffs across large corporations and companies reducing headcount. Will this happen again? Absolutely. But in most cases, these are cost-cutting measures wrapped in a different narrative, with AI often used as a convenient justification.

So who stays, and who’s at risk?

Engineers who understand that their role goes far beyond writing code—those who bring context, judgment, and clarity to ambiguous problems—are far more likely to remain valuable. On the other hand, those who rely solely on producing output without understanding why they’re producing it are the most vulnerable.

A stronger feedback loop

Will junior engineers be replaced? That’s something I plan to address in a separate post.

But one thing worth discussing is this: if AI handles a large part of code generation, can juniors still build judgment? I think they can—because AI significantly shortens the feedback loop.

Having spent over a decade in this industry, I remember the days of endlessly browsing Stack Overflow and flipping through programming books for answers. What once took hours or days now takes seconds. It may feel like skipping steps, but in reality, you’re just learning faster.

Consider this: you were hired before the AI wave because your company saw value in what you brought to the table. Now, with AI tooling, you’re significantly more productive. You ship faster, handle more complex scenarios, and deliver better outcomes.

It wouldn’t make much sense for a company to let you go simply because you’ve become more efficient at your job.

Staying ahead

If you’re already thinking about how to adapt, here’s where you can start:

  • Use AI tools: Whether it’s Claude, ChatGPT, Cursor, or something else—figure out what works for you and what doesn’t
  • Strengthen your actual role: Focus on understanding requirements, trade-offs, and communication with stakeholders
  • Learn systems end-to-end: The more you understand how a system works as a whole, the harder you are to replace
  • Document your work: Keep track of how you solve problems—it pays off later in your career
  • Stay open to learning: Being defensive or closed-minded will only slow you down. Embrace the tools and move forward

Conclusion

This field is changing rapidly. Tasks that once took days can now be completed in seconds. Some skills are becoming less relevant, while others are more important than ever.

If there’s one thing to take away from this, it’s this: your value was never in writing code. It’s in knowing what to build, why to build it, when to ship, and when to push back. It’s about solving the right problems—problems that actually help people.

Block browser features with permissions policy in Nginx

Recently, I learned that you can explicitly disable browser features like camera, microphone, and geolocation using the Permissions-Policy HTTP response header.

Using a single line in nginx, it does the job:

add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;

What this does?

  • Disables camera access
  • Disables microphone access
  • Disables geolocation access
  • Applies to all origins
  • The browser won’t even prompt the user for permission

The empty () means no origins are allowed to use these features.

Why this is useful?

  • Improves security and privacy
  • Prevents misuse by third-party scripts
  • Good default for content sites, admin panels, and APIs

The always flag ensures the header is sent even on error responses (404, 500, etc.).

Hope you found this tip useful!

Ubuntu Bootloader Recovery

This is a guide on how to recover Ubuntu's GRUB Bootloader in the case of whenever a LVM UUID is changed or corrupted.

The Problem

The error "disk not found: /xxx-xxx" in grub rescue> mode suggests GRUB can't find the device or logical volume it was previously configured to boot from.

This usually happens when: * LVM volumes weren't activated during boot * The volume group UUID changed or became corrupted

The Context

The /xxx-xxx in the message is likely referring to a LVM volume by UUID, e.g.:

error: disk 'lvmid/XXX-XXX-XXX' not found

This typically means GRUB is referencing a missing or renamed LVM LV or VG

The Solution

Boot from a live ISO, open a terminal and follow these steps:

1. Check Disks and LVM State

sudo lsblk
sudo fdisk -l

Then:

sudo vgscan

This activates any found LVM volume groups and logical volumes.

Then verify with:

sudo lvdisplay

3. Mount the System Manually

Assuming your root volume is /dev/mapper/your_lvm_drive:

sudo mkdir /mnt/recovery
sudo mount /dev/mapper/your_lvm_drive /mnt/recovery

4. Prepare for chroot

sudo mount --bind /dev /mnt/recovery/dev
sudo mount --bind /proc /mnt/recovery/proc
sudo mount --bind /sys /mnt/recovery/sys
sudo chroot /mnt/recovery

The chroot command changes the root directory for the kernel, effectively making a specified directory the new starting point for any file access within the chrooted process.

5. Reinstall GRUB

grub-install /dev/sdX  # Replace sdX with the actual disk (like /dev/sda)
update-grub

Exit chroot mode, unmount the recovery path from live-boot OS and reboot the system:

exit
sudo umount -R /mnt/recovery
sudo reboot

Hope you found this article useful!

Eliminate Laravel .env leakage between multiple sites on Apache (Windows)

Today, I learned something pretty important (and frustrating until I figured it out): if you're running multiple Laravel applications on Apache (Windows) using mod_php, you can run into a strange bug where one site's .env settings override the other.

This became painfully obvious when I noticed that one of my Laravel sites was randomly connecting to the wrong database, even though the configurations in each .env were different. Weird, right?

Turns out, the culprit was mod_php.

The problem

I had two Laravel sites hosted on the same Windows machine with Apache. Both were running fine — until they weren’t.

The issue? Sometimes one site would pick up the database config of the other. One minute I’d be on Site A, and then somehow its .env settings were coming from Site B.

This is just total chaos and worse, this issue existed from more two years until I decided to dive into the problem.

The cause

Laravel loads .env values during the bootstrap process and caches them in memory. When using mod_php, Apache loads PHP as a module once and shares it across all sites. That means only one copy of Laravel’s environment gets loaded — and reused across both apps.

So even if each app had its own .env, it didn’t matter. Apache was essentially saying:

“Hey, here’s PHP. I already booted Laravel with this config — just reuse it.”

The solution

After some digging, the solution is ditch mod_php and switch to FastCGI (mod_fcgid).

With FastCGI, each site runs its own instance of PHP via php-cgi.exe, which boots Laravel in isolation — meaning each .env file is respected per site.

How to configure it?

First, you need to disable mod_php in httpd.conf:

# LoadModule php_module "C:/path/to/php/php8apache2_4.dll"
# <FilesMatch \.php$>
#     SetHandler application/x-httpd-php
# </FilesMatch>
# PHPIniDir "C:/path/to/php/"

Then install mod_fcgid from Apache Lounge and copy mod_fcgid.so to modules/ directory and enable it in httpd.conf:

LoadModule fcgid_module modules/mod_fcgid.so
Include conf/extra/httpd-fcgid.conf

Next, you have to create/update httpd-fcgid.conf:

<IfModule fcgid_module>
   AddHandler fcgid-script .php
   FcgidInitialEnv PHPRC "C:/path/to/php"
   FcgidWrapper "C:/path/to/php/php-cgi.exe" .php
   Options +ExecCGI
   FcgidMaxProcessesPerClass 150
   FcgidMaxRequestsPerProcess 1000
   FcgidProcessLifeTime 300
   FcgidIOTimeout 120
   FcgidPassHeader Authorization
   FcgidFixPathinfo 0
</IfModule>

And then atlast, update your virtual host:

<VirtualHost *:443>
   ServerName example.com
   DocumentRoot "C:/path/to/website/public"

   <Directory "C:/path/to/website/public">
      Options +ExecCGI -Indexes +FollowSymLinks
      AllowOverride All
      Require all granted
      AddHandler fcgid-script .php
      FCGIWrapper "C:/path/to/php/php-cgi.exe" .php
   </Directory>

   SSLEngine on
   SSLCertificateFile "..."
   SSLCertificateKeyFile "..."
</VirtualHost>

Restart the nginx service and clear the laravel application cache:

httpd -k restart
php artisan config:clear
php artisan cache:clear

Bonus: Fixing 403 Forbidden Errors

After switching to FastCGI, I ran into a 403 Forbidden error on one site. That turned out to be a missing Options +ExecCGI and Require all granted in the <Directory> block. Once added, everything worked perfectly.

Result

Now, each Laravel site runs completely isolated, even though they use the same php-cgi.exe. No more .env bleed. No more wrong DBs. Just clean, independent Laravel environments — as it should be.

Takeaways

  • Don’t run multiple Laravel apps on Apache with mod_php unless you're okay with shared config risks.
  • Use mod_fcgid and php-cgi.exe for true isolation.
  • Even if using the same PHP version, FastCGI will spawn separate processes, so .env loading stays scoped.
  • Always check Apache error logs (error.log) and php_sapi_name() for confirmation.

If you’re running Laravel on Windows with Apache — this fix is a must.

Happy hosting and hope you found this article useful!

Removing Commits from a Branch Using Git Interactive Rebase

I'm working on a project that had approximately five minor incident fixes. However, I got approval from the client side to push only three of them. Luckily, my commits are atomic, so making this build was easier than I thought.

At this moment, I decided to clone the latest branch into a temporary branch and roll back two commits for today's deployment while keeping the latest branch intact with all the fixes.

To achieve this, I used Git interactive rebase, a powerful tool for modifying commit history.

Why Use Interactive Rebase?

Interactive rebase allows you to modify commit history, including:

  • Removing unwanted commits
  • Reordering commits
  • Squashing multiple commits into one
  • Editing commit messages

How I Did It?

Here’s the step-by-step process I followed:

1. Clone the Branch

Before making any changes, I cloned the branch to have a backup:

git checkout -b my-cloned-branch original-branch

2. Start Interactive Rebase

To remove specific commits, I ran:

git rebase -i HEAD~N

Where N is the number of commits I wanted to review. This opened an editor displaying the most recent commits.

3. Modify the Commit List

The interactive rebase interface showed something like this:

pick abc123 Commit message 1
pick def456 Commit message 2
pick ghi789 Commit message 3

To remove a commit, I replaced pick with drop.

To edit or squash commits, I could replace pick with commands like edit or squash.

After making my changes, I saved and closed the editor.

4. Handle Any Conflicts (If Needed)

If Git encountered conflicts, it prompted me to resolve them. I fixed the files and continued the rebase:

git rebase --continue

Key Takeaways

  • Interactive rebase is powerful for cleaning up commit history.
  • Always clone or create a backup branch before rebasing.
  • If working with a team, communicate before modifying shared branches.

This was a great learning experience, and now I feel more confident managing Git history efficiently!

Hope you found this tip useful!

Recovering MySQL After an Accidental Binlog Deletion

Last night, I found myself debugging a MySQL issue at an ungodly hour. The MySQL80 service refused to start, throwing a vague error:

The MySQL80 service started and then stopped…

Digging into mysql.err, I discovered the culprit—someone (not me! 😤) had accidentally deleted a binary log (mysql-bin.000xxx) while MySQL was still tracking it. Since MySQL relies on binlogs for replication and crash recovery, it panicked when it couldn't find the missing file.

The Fix

To get MySQL running again, I had to:

  • Disable binary logging (since this is a standalone server)
  • Delete the corrupt binlog index file (mysql-bin.index)
  • Purge all old binlog files that were hogging disk space

How to Properly Disable Binary Logging in MySQL 8.0?

Since MySQL 8.0 enables binlogs by default, simply removing log-bin=mysql-bin from my.ini wasn’t enough. I had to:

Comment out the existing binlog setting in my.ini

# log-bin=mysql-bin

Explicitly disable binary logging

[mysqld]
skip-log-bin

Delete the old mysql-bin.index file

  • This file keeps track of all binary log files.
  • Since a referenced binlog file was deleted, MySQL would fail to start if the index still contained entries for missing files.
  • Deleting it ensures that MySQL doesn’t look for non-existent logs.

After restarting the service, I confirmed binlogging was off with:

SHOW VARIABLES LIKE 'log_bin';

Output:

log_bin | OFF

In conclusion, crisis averted, disk space reclaimed and finally got some sleep. 😴

Hope you found this useful!

9 Software Books I'm Reading This Year to Become a Better Engineer

As a software engineer, continuous learning is essential. The tech industry evolves rapidly, and keeping up requires a commitment to improving skills, refining best practices, and understanding the deeper principles behind great software design. This year, I’ve curated a list of nine must-read books that will help me become a better developer, architect, and problem solver.

If you're looking to level up your software engineering skills, these books might interest you too:

The Pragmatic Programmer

By Andrew Hunt & David Thomas

"Care about the code you write and the people who will maintain it after you."

A foundational book that every developer should read. It provides practical advice on coding, debugging, and automation, making you a more adaptable and efficient engineer.

Designing Data-Intensive Applications

By Martin Kleppmann

"A well-designed system grows gracefully as the dataset, traffic volume, or complexity increases."

A must-read for backend engineers and system architects, this book explores data modeling, distributed systems, and scalability challenges.

Clean Code

By Robert C. Martin

"Indeed, the ratio of time spent reading versus writing is well over 10 to 1. We are constantly reading old code as part of the effort to write new code."

Writing clean, maintainable code is a core skill for developers. This book offers practical techniques for improving code quality.

The Mythical Man-Month

By Frederick P. Brooks Jr.

"Adding manpower to a late software project makes it later."

A classic in software project management, this book explains why adding more people to a late project only makes it later.

Refactoring

By Martin Fowler

"Any fool can write code that a computer can understand. Good programmers write code that humans can understand."

A practical guide to improving existing code without altering its functionality, making it more readable and maintainable.

Domain-Driven Design

By Eric Evans

"The heart of software is its ability to solve domain-related problems for users."

This book teaches how to align software design with business logic, making complex software systems more manageable.

Working Effectively with Legacy Code

By Michael Feathers

"To change software means to change behavior. Our goal is to do so safely."

If you’ve ever had to deal with old, complex codebases, this book provides strategies for refactoring without breaking functionality.

Why Programs Fail

By Andreas Zeller

"Every bug has a cause, and every cause has a cure."

A systematic approach to debugging, helping developers diagnose and fix software defects effectively.

Extreme Ownership

By Jocko Willink & Leif Babin

"Leaders must own everything in their world. There is no one else to blame."

Leadership and accountability are critical for engineers working in teams. This book teaches how taking full responsibility leads to success.

Final Thoughts

These books cover a wide spectrum of skills—from writing clean code and designing scalable systems to debugging, refactoring, and leadership. By reading and applying the lessons in these books, I aim to become a better software engineer this year.