Getting AWS SES production access in 2026

1 February 2026

Working with Amazon Web Services SES has been anything but smooth.

I’ll be blunt: if you’re a solo builder in 2026, AWS SES is not built for you. They don’t target you. They don’t prioritize you. And they definitely don’t respect your time.

This post is not theory. It’s lived experience.


The First Rejection (No Explanation)

I followed the documentation.
I submitted the production access request.
I waited.

Then: rejected.
No reason. No feedback. Nothing.

So I did what most solo founders end up doing. I posted on Reddit. Support noticed. Someone reached out. The SES team “reviewed” it again.

Same result.
Rejected.
Still no explanation.


The Second Attempt (I Did Everything Right)

At this point, I assumed I was the problem.
So I went all in.

I spent hours making the setup bulletproof.

What I implemented (properly):

Authentication & Compliance

  • Domain verified

  • SPF passing

  • DKIM signing

  • DMARC upgraded from p=none → p=quarantine

Bounce & Complaint Handling

  • SES account-level suppression

  • Application-level suppression (Rails model)

  • SNS topics wired correctly

  • Simulator tested (bounce + complaint)

  • Future sends blocked automatically

Monitoring

  • CloudWatch dashboards

  • Alarms for bounce >5%, complaint >0.1%

  • Weekly review plan

Abuse Prevention

  • Cloudflare bot filtering

  • Cloudflare Turnstile on signup

  • Email verification mandatory

  • Dedicated abuse/contact page

  • 24–48h response SLA

Business Clarity

  • Strictly transactional emails only

  • No marketing

  • No newsletters

  • No list buying

  • No scraping

  • MVP stage

  • ~10 emails/day

  • Conservative warm-up schedule

I even attached screenshots proving simulator tests and suppression logic worked.

This is textbook SES compliance.
Probably better than most production setups.


And Then… Silence

No rejection.
No approval.
No questions.

Just… nothing.

More than a week.
A complete ghost.


Meanwhile, this blocks my launch.
Momentum stalls.

This is the part that hurts the most.


The Hard Truth About AWS SES

It is not optimized for:

  • Solo founders

  • Early-stage SaaS

  • Builders trying to ship fast

  • Developers who need predictable support

And that’s fine.


But AWS should be honest about it. They aren't. They should highlight it as requirement, instead they encourage SES users to request production access. Thus wasting their efforts and time.

Right now, the process feels opaque, arbitrary, and hostile to small builders.


What I’d Tell a Solo Builder in 2026

If you’re early-stage:

  • Avoid AWS SES

  • Use a developer-friendly email service provider

  • Pay a bit more

  • Ship faster

  • Sleep better


People tell me SES might make sense later. When you have scale. When you have leverage, but honestly, if the experience is so poor when you are starting out, then why would you rely on them later?

It’s not worth the time sink.


Final Thought

I don’t regret learning the internals. I regret trusting the process. I regret loosing a bit of momentum because of it.

If this post saves another solo builder a week of lost momentum, it’s done its job.

Onward.

— Rahul


PS: This isn’t the end. Part 2 covers the in depth details of the request process, escape hatch: better tools, faster setup, and how to unblock your launch without begging for approval.

Read more ...

AI Hallucinations Are Real and the Hype Is Getting Loud

11 January 2026

As a Solo Builder, Solopreneur, Entrepreneur, whatever label fits - you can’t avoid social media. It’s one of the strongest channels for organic growth.

So naturally, when I open my feed, I see a lot of posts that feel completely out of reach.
Not aspirational. Just disconnected.


Hard to fathom, because my own lived experience simply doesn’t resonate with what’s being claimed.


As I sit here trying to integrate a payment gateway for ThaiCopilot.com, I’m doing what usually works for me: asking Claude to plan, Gemini to review, switching models, switching sessions, using whatever technique gets me unstuck.

This works for simple features.

Suddenly, it doesn’t.

And this isn’t my first payment integration. I’ve built payment systems from scratch before. Many times. I know how these systems need to be designed and implemented. I know where things go wrong. I know what questions matter.

And AI is just not good enough.

I tried multiple sessions.
Multiple models.
Different prompts.

None of them worked.


Yet I keep hearing that software engineering is dead.

That triggers me.

Because people who used to code by hand should be considered a goldmine. AI will hallucinate. It hallucinates confidently. It spits out nonsense with authority.

Maybe it will improve.
Maybe it will reach AGI.
Maybe it will become super-intelligent and humans won’t have to do anything.

But that path also risks degrading humans as a species.

If everyone relies on AI, nobody learns. Nobody builds intuition. Nobody thinks deeply. Why bother learning when you can just ask AI?

AI as a tutor might end up having too much power.

We’re not there yet.

But we can already see early signs.


Open LinkedIn.

Everyone is going gaga over AI. Founders. Investors. Engineers. Marketers. Pick a profession, everyone has a hot take. Everyone is claiming something.

What’s really happening is this: some people are picking up AI too early, some too late, and some because they lack fundamentals. They feel AI can do everything, so human skill no longer matters.

AI can write a ton of content.

Does that make writers irrelevant?

What about lived experience? Does AI have 30, 40, 60 years of lived experience?

It doesn’t.





It has data. It predicts the next token. Yes, it’s inspired by the human brain. But what is it actually doing? Does anyone truly understand it?

I don’t. And I’m okay admitting that.

So when a “noob” like me opens LinkedIn and sees endless AI-generated content, it feels off. Some of it even feels sponsored or manufactured hype to attract more investment, especially on LinkedIn.


Sure, AI has its place. It’s too good to ignore.

For the last year and a half, I’ve used AI consistently at work. One of the great things about working at Agoda was company-wide AI adoption. Leadership believed in GenAI. Employees were empowered to explore.

AI was always a topic - town halls, tech talks, leadership sessions.

At one point, I relied on AI too much.

And I noticed something uncomfortable.

My own growth stalled.
My thinking weakened.
My problem-solving muscles softened.

Then Max gave an internal tech talk. One thing he said stuck with me. I’m paraphrasing, but the idea was clear: research shows that over-reliance on AI reduces cognitive capability.

AI can make you lazy.

But if you use AI as a guide, as a thinking partner, as something that sharpens your reasoning instead of replacing it, it can actually improve your cognition. It can be a great force multiplier.

We should not be in the first camp.

We should be in the second.

Max helped me validate my thought process and from there it was an upward trajectory.


Interestingly, the second camp isn’t loud. They aren’t  constantly posting on social media sites like LinkedIn. They aren’t flooding everyone's post with AI-generated posts.

They’re building.

I think I fall into that camp.

That’s why I want to write about this. Not to preach. Not to predict the future. Just to document what I’m seeing, in real time, from the trenches.

AI is powerful.

But it is not a replacement for experience, judgment, or deep work.
At least not yet.

And pretending otherwise is dangerous.


What do you think?

What’s been your experience from the trenches?




All comments and opinions are welcome.

Read more ...

Getting Started with Terraform - Solo Builder Edition

4 December 2025

Terraform sounded promising when I first heard about it, so I did what everyone does: read the docs, overthought it, and procrastinated. Eventually I stopped reading and started using it. That changed everything - Claude, I'm staring at you.


Bangkok City's Infrastructure, a view!



Now it’s the only sane way I know to manage infrastructure. No more clicking around AWS or GCP like it’s 2012.

Here’s the shortest path to getting productive without drowning in IaC philosophy.


1. Install three things

  1. Terraform.
  2. AWS CLI (or gcloud).
  3. A folder you won’t abandon.
Done.


2. Start with the smallest possible main.tf

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

provider "aws" {
  region = "ap-southeast-1" # choose nearest one to your users
}

resource "aws_s3_bucket" "app" {
  bucket = "my-terraform-demo-bucket"
}

Keep it boring. Boring infra is stable infra.


3. Run Terraform’s holy trilogy

terraform init
terraform plan
terraform apply

Init downloads what you need.
Plan tells you what’s about to happen.
Apply is what changes your infrastructure's actual state. 

That’s most of Terraform.


4. Add variables when repetition creeps in

variable "region" {
  default = "ap-southeast-1"
}

Then:

provider "aws" {
  region = var.region
}

5. Add a .gitignore before you leak your entire cloud

.terraform/
*.tfstate
*.tfstate.*
*.tfvars
*.tfplan

State files are radioactive. Never commit them.


6. Use outputs so you stop clicking around consoles

output "bucket_name" {
  value = aws_s3_bucket.app.bucket
}

Terraform prints what you need. No detective work.


7. The “init -upgrade” 

Whenever you add a new provider, run this immediately:

terraform init -upgrade

This is not optional. Terraform will instantly warn you. This is how you avoid Terraform’s mood swings.

Real example from my setup:
I added the null provider:

terraform {
  required_providers {
    null = {
      source  = "hashicorp/null"
      version = "~> 3.0"
    }
  }
}

And used it for a tiny bootstrap step:

resource "null_resource" "bootstrap" {
  provisioner "local-exec" {
    command = "echo hi"
  }
}

Terraform immediately yelled:

Error: Inconsistent dependency lock file
provider registry.terraform.io/hashicorp/null:
required by this configuration but no version is selected

Translation:
“You added a provider. It’s not in .terraform.lock.hcl.
I refuse to do anything until you fix this.”

The fix:

terraform init -upgrade

It pulls the provider, updates the lock file, and Terraform stops sulking. 


8. Manage all providers with Terraform

Whenever you need to setup your infrastructure weather it's Public Cloud like AWS, GCP or external tools like New Relic, Rollbar, whatever. Terraform got you covered. 

That’s the real power here. One workflow. One config. One state file. Everything managed the same way. Let me know, how far you can take this setup? Happy to hear from fellow terraform devs.


9. Grow slowly, not heroically

Start tiny. Add resources one at a time.
Split files when things get crowded.
Terraform rewards momentum, not architecture diagrams.


The Point

Terraform isn’t “DevOps tooling.”
Terraform is “please don’t break production at 2 A.M.” tooling.

For solo founders, it gives you:

  • Reproducible infra
  • Real version control
  • Zero mystery changes

Start small. Keep it clean. Let Terraform scale with you. Now go build and conquer the world! 

Read more ...

Writing Again. Finally!

26 November 2025

When I was in college, I was learning fast and publishing often. Once work started, the routine changed. Most days I didn’t have the energy to write anything, and the habit slipped away. I kept convincing myself I’d return to it eventually, but the years just moved on.

My last post was on 7 February 2016. Today is 26 November 2025. Nearly a decade. Long enough to forget what my own writing sounded like.

I’ve been wanting to get back to writing for a while now, mostly for myself. I miss having a place to think things through, collect ideas, and look back on what I was paying attention to at different points in my life.

So this is me picking the habit up again. No big themes yet. No grand plans. Just writing a little more regularly and seeing where it goes.

If nothing else, it will be interesting to track how my thoughts change over the next year.


A picture of Chao Phraya River, Bangkok


If you’ve also gone through long gaps in writing or making things, feel free to share. It would be interesting to hear how you got back into it.

More posts soon. See ya!

Read more ...

Solving Project Euler Problem 1

7 February 2016
In this series, we'll be solving Project Euler problems. First using brute force approach(if possible), then using optimized approach. Let's get started!





Project Euler Problem 1 states that:

If we list all the natural numbers below 10 that are multiples of 3 or 5, we get 3, 5, 6 and 9. The sum of these multiples is 23. Find the sum of all the multiples of 3 or 5 below 1000.

Let's make it more generic, instead of finding the sum of all the multiples of 3 or 5 below 1000, let's find the sum for any number. The number could range from 0 to millions. Our program should be able to handle it. We will verify this behavior with test cases.

Okay! I'm going to take object oriented approach here to organize the code, but feel free to skip the object oriented part, if you are not cool with it.

Let's quickly add the basic skeleton.

  class DivisibleBy3And5
    def initialize(number)
      @number = number
    end

    def sum
      23
    end
  end


Our test skeleton

  describe "divisible by 3 or 5" do
    def numbers(number)
      DivisibleBy3And5.new(number)
    end

    it "should return 23" do
      expect(numbers(10).sum).to eq(23)
    end
  end


Let's go through each chunk of code one by one.

First of all, we have a DivisibleBy3And5 class, which is taking a number on initialization and storing it @number instance variable, so that we don't have to pass the number around to each method.

Next we have 'sum method, which currently just returns a static value(23).

Moving on to our test code. The numbers method is just there to initialize our class DivisibleBy3And5 so that we can use simply pass number to our numbers method, have it return an instance of our DivisibleBy3And5 class so, that we could easily call our other instance methods like sum to get the sum. This approach makes our test code concise and easier to read.

Our skeleton looks good and gives a clear picture of the overall system, but it's not very useful, so let's make it more useful.

Brute Force Solution


  class DivisibleBy3And5
    def initialize(number)
      @number = number
    end

    def sum
      return 0 if @number < 3
      calculate_sum
    end

    def calculate_sum
      sum = 0
      (3..@number-1).each do |number|
        sum += number if divisible?(number)
      end
      sum
    end

    def divisible?(number)
      number % 3 == 0 || number % 5 == 0
    end
  end


And our test suite:

describe "divisible by 3 or 5" do
  def numbers(num)
    DivisibleBy3And5.new(num)
  end

  it "should return 23" do
    expect(numbers(10).sum).to eq(23)
  end

  it "should return 2318" do
    expect(numbers(100).sum).to eq(2318)
  end
end


Run the tests and it's passing. Great!

This was simple enough. Our brute force approach helped us get started with the problem in hand without getting overwhelmed, but we can optimize this to improve performance.

What's the complexity of our brute force approach, you asked? Given the number N, we are iterating through each number, incrementing sum by adding the number, if the number is divisible by either 3 or 5.

Checking if the number is divisible by 3 or 5 takes constant time, so does the adding number to sum variable, but we are performing N number of constant operations for a given number N, so the big-oh complexity of our brute force approach is O(N). Simple!

Optimized Approach

Step back for a moment, and think about the problem. Does it follow some kind of pattern? I'll let you think before proceeding.


Hint: It's a mathematical problem, so it involves some kind of mathematical expression.


Okay! Let's start breaking down the problem statement.


List all the numbers below the number N that are multiples of 3 or 5. Let me rephrase it. List all the numbers below the number N  that are multiples of 3

3, 6, 9, 12, 15, 18, 21, 24, 27, 30, ...

or all the numbers below the number N that are multiple of 5

5, 10, 15, 20, 25, 30, 35, 40, ...


There are two things to notice here.

  1. Each sequence(let's call it S3 & S5) forms a series called arithmetic series.
  2. They have some numbers in common. 15, 30, 45, 60, ... in other words they have an arithmetic series in common with the difference between each element being(d) 15 and first element being 15(a).
So we need to add the sum of first series(S3) & sum of second series(S5) and finally subtract the sum of third series(S15) from the sum of first two(i.e. S3 & S5).

In mathematical term, it can be represented as Sum of S3 + Sum of S5 - Sum of S15


See, how dividing problem into smaller pieces, makes the solution easier?

Enough Math! Let's write some code.

  class DivisibleBy3And5
    def initialize(number)
      @number = number
    end

    def sum
      calculate_sum(3) + calculate_sum(5) - calculate_sum(15)
    end

    def calculate_sum(divisor)
      n = (@number - 1).div divisor
      divisor * n * (n + 1)/2
    end
  end


Everything looks simple enough except calculate_sum method, which requires some explaining.

For number 100, the value of N would be 99, as it's the largest number, which is divisible by 3 and is still less than 100. So, to the sum for S3 series would be (3 + 6 + 9 + ... + 99), since 3 is common we can extract it, so our series becomes 3 * (1 + 2 + 3 + 4 + ... + 33).

Now the (1 + 2 + 3 + 4 + ... + 33) part is particularly interesting. It forms a sequence of first N numbers, where N is 33. What's the sum of first N natural numbers? It is N(N+1)/2. So, to calculate the sum of (3 + 6 + 9 + ... + 99), we are simply multiplying 3 to N(N+1)/2, which is exactly what we are doing at line 15.


So, to achieve all of this, we are calculating the largest natural number for 1 + 2 + 3 + ... series at line 14, at line 15 we are simply multiplying divisor(3, 5 or 15) by N(N+1)/2 to get the sum of original series.

Using this approach we have been able to avoid iterating over N numbers, which results in constant time complexity i.e O(1). With our new and optimized code, we can easily handle millions of numbers.


PS: The code listed in this blog post is hosted on Github. Feel free to propose any change by submitting a pull request or by opening an issue.

Read more ...

Technology Agnostic Interview for Web Developers

20 January 2016
One of my friends works as a Java Developer and he was looking for a job change. As a result he was involved with a couple of startups, and for each startup he was learning and doing the assignments in Language/Framework supported by these companies.

One thing I noticed that he was preparing for Language/Framework oriented interviews rather than a generic one, even though he had no professional experience with those Languages & Frameworks.

Here is what's wrong with his approach. While knowing the Language/Framework might give you an edge over other candidates, you should be preparing for a generic web developer interview if you have never worked with [insert Language/Framework].


                                    


How I would conduct(or prepare for) such interviews? I would list out things which are common in web development irrespective of Language/Framework.

So, let's begin listing out these common concepts(this list isn't exhaustive, use it as a guidelines):

  1. Architecture: RESTful Architecture, MVC pattern etc. 
  2. Design Patterns 
  3. Refactoring Techniques
  4. Databases
  5. Client side stuff
  6. Basic Linux Commands, Server Management & Deployment Techniques 
  7. Version Control 
  8. Web Security
  9. Testing

1. Architecture

Here you can ask all sorts of questions related to scaling, code architecture, etc. depending on candidates experience.

Also, I would only expect them to know architecture they have been using all along. MVC architecture is a very good example of that. Example of frameworks which follows the MVC pattern are Ruby on Rails, Django, ASP.NET MVC, Laravel etc.

Here's a list of questions to begin with:
  • What is REST? Can you explain it with example? - I'll look out for basic understanding of how urls are organized using RESTful architecture, purpose of various HTTP requests types etc.
  • How does a typical monolithic application laid out? Can you describe the major components? - Here I expect them to describe various components of the application like databases, application servers, web servers, caching servers, etc.  
  • How would you scale a monolithic application? Where would you start? - Here I expect them to mention common performance issues. This is very generic. Common optimization technique includes Database optimization, microservices, caching, code cleanup, serving Static content over CDN etc.
  • What are the advantages of frameworks following patterns like MVC vs barebones framework which doesn't enforce any pattern? 
Note: I won't care if they don't know what these fancy words like monolithic, microservices etc the important thing is that have they ever thought about it? If not can they?

2. Design Patterns

I won't judge if they aren't aware of any pattern. It's quite possible for junior web developers to be not aware of design patterns that they been using all along.

If they are a fan of a particular design pattern, then I would ask them to explain it and understand why they like it over other design patterns or in which situation a particular design pattern fits in?

3. Refactoring Techniques

You generally want to understand how would they go about refactoring their code base. What are the general techniques they employ to improve their code. I expect them to mention basic techniques like DRY, KISS, YAGNI, keeping variable names readable, keeping methods short and concise, keeping return data predictable etc.

4. Databases

As a backend developer, you should know the basics of SQL, but if you've been working with ORMs like ActiveRecord and you can write relatively complex queries (using joins, includes, associations & methods provided by ORM) then it's fine too! This is one of the things which depends on requirements as well as candidate's experience. If your code base has lots of complex non ORM SQL queries, then you can focus on SQL part as well.

5. Client Side Stuff

Since the focus of this post is on Web Developers rather than Web Designers I would ask things like how cookies & sessions work, CORS, when to use local storage, web sockets, javascript basics etc. Here's a small list of questions to test against:

  • Difference between Sessions & Cookies? And How they work?
  • Explain CORS?
  • What is web sockets and polling? Use case for web sockets?
  • How do you organize your JavaScript code?

6. Basic Linux Commands, Server Management & Deployment Techniques 

The candidate should not be scared of terminals. You should have basic knowledge of Linux commands, if you don't, then try this The Command Line Crash Course

Here's a small list of questions you can use to evaluate them:

  • How to navigate between directories, create them, remove them using command line.
  • Creating, deleting, updating files from command line.
  • How to ssh into remote machine? What the steps required?
  • Handy server management tools? Backups, Snapshots, releases, symlinks, shared directories etc
  • Why do you need deployment scripts? Favorite deployment tools.
  • How to setup a server from scratch?
  • Difference between app server & web server.


7. Version Control 

I would ask which version control system they have been using and what was their usual work flow. How often they commit, how they keep commit message short and concise, and other basic tasks performed. Here's the basic starting point for git:

  • How would you setup a repository using git(or your favorite version control system)? - If they have ever used a version control, then should be able to explain it.
  • Why do you use version control system? Advantages & Disadvantages?
  • How do you create branches? Why do you need them?
  • How do you resolve merge conflicts?
  • How would you revert a commit that has already been pushed?
  • Do you use aliases? 
  • How do you check logs and diffs?
  • Difference between merge, squash merge & rebase?
  • Most handy and least popular feature of git?
The basic idea is to check if they have been using version control effectively or not and learn a thing or two from them.


8. Web Security

The candidate should be aware of basic security issues like SQL Injection Attack, Cross-site Request Forgery, Why should you keep your libraries up to date and how to manage this? Server side validations vs Client side validations and why client side validations can't be trusted? Whitelisting attributes, etc.

Checkout Ruby on Rails Security Guide to get the general idea. Ignore Rails specific stuff, most of it applies to general web development.

9. Testing

If you're still very early in your career, then testing won't make much sense to you (trust me, I've been there), but over time it'll grow on you I know it because it certainly has for me and some of my friends in similar domains.

Ideally you should be able to describe basic testing setup, how to write unit tests, when to test, and basic ideas behind unit testing. Integration tests are important too, but they are bit advance, so I'll leave that up to you to.



I know these are fairly basic stuff, but these are the most generic things I can think of in web domain. You can always dig down deeper, but this list should give you a starting point, and that's the whole point.  You can use this list to conduct telephonic round, then ask them to solve a real world problem at their own time.



Here are the techniques you can use to dig deeper:
Assignment: You could ask candidates to build a quick prototype of some idea, so that you can get the feel of their coding style and standards.

Pair Programming Session: This will allow you to have a closer look at how the candidate behaves in a real world scenario.

Contract based work: How about hiring them on a contract? This way you'll definitely know, if they'll fit in your team or not.

That's it!


Your Turn?
As always feel free to suggest more generic stuff that I might have missed and how would you go about it?

Read more ...

Optimising Git workflow with Git Aliases

29 April 2014
Git aliases is a good way to save few keystrokes and improve your productivity. So, in this post I will show you how to improve your productivity with git aliases.

Optimising Git workflow with Git Aliases


To customize git commands, you need to edit .gitconfig file located in your home directory(valid for *nix system, not sure about windows). So, without further ado let's get started.

Open .gitconfig file with your favorite text editor.

subl ~/.gitconfig


Now inside this file add the following lines


[alias]
    st = status
    aa = add -A
    cl = clone
    ci = commit
    df = diff
    br = branch
    co = checkout
    cob = checkout -b


Now you can use these aliases to save few keystrokes. For example you can use "git co feature_branch" instead of "git checkout feature_branch" to switch  to feature_branch, "git st" instead of "git status" to determine the current status of your working directory.

Now let's customize  one of  the most used git command i.e 'git log'.

lo = log


but this default log command is boring, so let's improve it.

lol = log --oneline


Much more use concise, but I'm not happy with formatting, so let's customize it even further.

lola = log --pretty=oneline --decorate --abbrev-commit -all


Looks good, but let's improve it even further with --graph option.

lola = --graph --pretty=oneline --decorate --abbrev-commit-all

git log ailas lola














Cool! You can even customize it even further with colors and more information like auther name, email, time etc.  Use this guide to customize it even further.


It would be nice to set an alias to list all the alias, so let's do it. Add following line in your .gitconfig file.

la = "!git config -l | grep alias | cut -c 7-"



Now running "git la" will list all of your git aliases. That's it. Are you using git aliases to improve your productivity? If so, then let me know what's your favorite git alias.


Reference: https://git.wiki.kernel.org/index.php/Aliases
Read more ...