惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Engineering at Meta
Engineering at Meta
D
DataBreaches.Net
云风的 BLOG
云风的 BLOG
B
Blog
T
The Blog of Author Tim Ferriss
MyScale Blog
MyScale Blog
A
About on SuperTechFans
H
Heimdal Security Blog
AI
AI
F
Full Disclosure
The Last Watchdog
The Last Watchdog
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
量子位
I
InfoQ
Martin Fowler
Martin Fowler
MongoDB | Blog
MongoDB | Blog
WordPress大学
WordPress大学
Hugging Face - Blog
Hugging Face - Blog
小众软件
小众软件
N
News and Events Feed by Topic
腾讯CDC
L
LINUX DO - 最新话题
Attack and Defense Labs
Attack and Defense Labs
Hacker News: Ask HN
Hacker News: Ask HN
Google Online Security Blog
Google Online Security Blog
博客园_首页
Forbes - Security
Forbes - Security
The Register - Security
The Register - Security
博客园 - 三生石上(FineUI控件)
PCI Perspectives
PCI Perspectives
V
Vulnerabilities – Threatpost
The Cloudflare Blog
酷 壳 – CoolShell
酷 壳 – CoolShell
Recent Commits to openclaw:main
Recent Commits to openclaw:main
L
LangChain Blog
Security Archives - TechRepublic
Security Archives - TechRepublic
NISL@THU
NISL@THU
博客园 - Franky
Microsoft Security Blog
Microsoft Security Blog
J
Java Code Geeks
Simon Willison's Weblog
Simon Willison's Weblog
O
OpenAI News
H
Hacker News: Front Page
Project Zero
Project Zero
P
Privacy International News Feed
Cyberwarzone
Cyberwarzone
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
大猫的无限游戏
大猫的无限游戏
Application and Cybersecurity Blog
Application and Cybersecurity Blog

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant Common SOC 2 Failures (Real World) Stop Vibe-Checking Your AI App: A Practical Guide to Evals How to Use SonarQube and SonarScanner Locally to Level Up Your Code Quality Your Next To-Do App Is Dead — I Replaced Mine with an OpenClaw AI Sign a Nostr event in 60 lines of Python using coincurve — no nostr-sdk, no nbxplorer, no rust toolchain ITGC Audit Explained Like You’re in Big 4 Patch Tuesday abril 2026: Microsoft parcha 163 vulnerabilidades y un zero-day en SharePoint Stop scraping everything: a better way to track competitor price changes Listing on MCPize + the Official MCP Registry while routing payments OUTSIDE the marketplace — how I kept 100% of my x402 revenue Building an AI-Powered Risk Intelligence System Using Serverless Architecture Why We Ripped Function Overloading Out of Our AI Toolchain Testing AI-Generated Code: How to Actually Know If It Works SaaS Churn Is Killing Your Business. Here Is What to Do About It (Without a Support Team) The Speed of AI Is No Longer Linear - And Self-Improving Models Are Why How to Implement RBAC for MCP Tools: A Practical Guide for Engineering Teams From Standard Quote to Persuasive Proposal: AI Automation for Arborists I built a CLI that scaffolds complete multi-tenant SaaS apps Axios CVE-2025–62718: The Silent SSRF Bug That Could Be Hiding in Your Node.js App Right Now The dashboard that ended our friendship Data Pipelines Explained Simply (and How to Build Them with Python) The Hidden Cost of AI Systems Nobody Talks About. undefined vs undeclared, and how typeof behaves Switching from file-based jobs to NATS/Kafka in Rust without changing code io_uring Adventures: Rust Servers That Love Syscalls Why Agentic AI is Killing the Traditional Database The POUR principles of web accessibility for developers and designers Quantum Neural Network 3D — A Deep Dive into Interactive WebGL Visualization How To Install Caveman In Codex On macOS And Windows Automation Pipeline Reliability: Why Your Workflow Breaks When Nobody Is Watching I Built an 'Open World' AI Coding Agent — It Works From ANY Folder From Freelancing to Product: A Tech Service Company's SaaS Transformation China's AI Giants: Adding Tencent Hunyuan & ByteDance Doubao to AI University (74 Providers) On the Vibe Coders and Their Lies clerk: Auto-Summarize Your Claude Code Sessions AI Weekly — 2026/04/10–04/17 | The Model Lockdown Is Here, but the Toolchain Is the Real Battleground AI 週報 — 2026/04/10–2026/04/17 模型封鎖潮來了,但工具鏈才是真戰場 Maybe this is how Open-Source apps are born... 🚀 Fine-Tune LLMs with LoRA and QLoRA: 2026 Guide tRPC v11 + Next.js App Router: End-to-End Type Safety Without the Boilerplate ShadCN UI in 2026: Why I Stopped Installing Component Libraries and Started Owning My Components SaaS Billing in React Server Components: Stripe + Supabase Without a Single `useEffect` Join our DEV Weekend Challenge — $1,000 in Prizes Across TEN winners! Submissions Due April 20 at 6:59 AM UTC. Implementing FSRS Spaced Repetition in Flutter + Supabase — Adding Memory Science to an AI Learning App "I Texted My Localhost From the Train — Claude Code Fixed the Bug Before I Got Home" I Built a Sales Prep AI and It Went Deeper Than Expected Design to Code #2: One JSON, Eleven Outputs Solving the 100M-Row Problem: A Summary Table Pattern for High-Volume Push Notification Logs Flutter Web With Wasm: What Actually Changes For Developers I Built 50 Royalty-Free Soundtracks for My Side Project in a Weekend Using AI Music Generation The Vibe Coding Security Checklist: 7 Things to Check Before You Ship Stop Letting Googlebot Guess Fix Your React App's SEO Right Desconstruindo o Streaming do LinkedIn: Como Criar um Engine de Extração de Vídeo de Alta Performance com HLS e FFmpeg (EDA Part-1) EDA (Exploratory Data Analysis) Explained With Real Life — Why Looking at Your Data Is the Most Important Step in Machine Learning Brand Relationship Management at Scale: Our 4-Touch Outreach System for 200+ Brands Why String.fromEnvironment() Might Return an Empty String in Dart JGuardrails 1.0.0 — Hardening Java LLM Apps Against Jailbreaks, Toxicity, and Prompt Injection Plan and Schedule a Full Week of Threads Content From One Claude Conversation Coding Cat Oran Ep3, Five Tables Changed Everything Updated: BFF Pattern I'm done watching freelancers get buried by 200 proposals. So I'm building the alternative. This is my first post BFS Algorithm in Java Step by Step Tutorial with Examples Tracking LLM Pricing Monthly: An Open Dataset for 22 AI Models How We Measure Content ROI on a Comparison Site: Revenue Attribution Without Perfect Data Introducing Nova AI Ops: The AI-Native Operating System for SRE Teams I built a free desktop video downloader for Windows — Grabbit How Talkie OCR Helps Vision-Impaired & Dyslexic Users Read the World Around Them VRCFaceTracking安装和iPhone面捕配置教程,有bug Even CrowdStrike Can't See Your Agents The Automation Gold Rush: What n8n Workflows and Claude Are Opening Up for Developers Right Now
From PHP to Go: what took me longest to rewire
Anatolii · 2026-06-23 · via DEV Community

I wrote PHP for about seven years before Go became my main language — Laravel for five of them, Yii2 and plain MVC before that. Then I led the rebuild of a Laravel monolith into Go microservices, and later joined a marketplace-product, to work on Go services in production. So I didn't come to Go from a tutorial. I came to it carrying a decade of PHP habits, and I had to ship real systems while unlearning them 🥲

The syntax was the easy part. You can read Go in an afternoon. What took months to rewire were the mental models — the default assumptions PHP had built into me about how a program is shaped, how errors move, how a request lives and dies. Some of those assumptions are actively wrong in Go, and they don't announce themselves 😫. They show up as code that compiles, passes review on a tired day, and then behaves in a way you didn't predict 😳

This is a list of the ones that took me longest. Each is tied to something I actually built, not a textbook example.


1. There is no try/catch, and that is a feature, not a missing one

In PHP I threw exceptions and caught them somewhere up the stack — often far up the stack, in a global handler that turned anything unexpected into a 500. The mental model is: errors travel invisibly until someone decides to look. Most of my code didn't think about failure at all; failure was something that happened to the call stack, above me.

Go inverts this. A function that can fail returns an error as its last value, and you, the caller, deal with it right there 🥳:

user, err := repo.FindUser(ctx, id)
if err != nil {
    return fmt.Errorf("find user %d: %w", id, err)
}

My first instinct was that this was noise. Coming from try { ... } catch (\Throwable $e) {}, the if err != nil after every call felt like ceremony 😅. It took me a while — and a few production incidents — to understand what it buys you: failure becomes part of the visible control flow. You can't not see that a call can fail, because the error is sitting in a variable in front of you. The decision "swallow this, retry this, or pass it up" is made at the exact place that has the most context to make it.

The habit that took longest to kill was the urge to build a catch-all. In PHP I leaned on the global exception handler. In Go I had to learn to wrap errors with context as they go up (%w and a short message at each layer) so that by the time an error reaches the top, the message is a breadcrumb trail — "find user 42: query timeout: ..." — instead of a stack trace I have to decode 🥳

This paid off in a real incident. An order wouldn't create, and the wrapped error that came out the top read essentially like this:

create order 8842: charge payment: gateway timeout after 5s

That one line pointed straight at the cause — a downstream service we were calling to charge the payment was misbehaving and timing out. No log spelunking, no guessing which layer failed. The message had assembled itself from each layer adding a little context on the way up, and by the time it reached me it told me exactly where to look. In my PHP days that would have surfaced as a generic 500 and a stack trace I'd have to read backwards 🥲

What I'd tell a PHP developer: stop looking for try/catch. The if err != nil is your error handling, and writing it everywhere forces you to actually think about each failure instead of deferring all of them to one handler that prints "Something went wrong." 👍


2. The request is not the unit of life anymore

This was the deepest shift, and the one I underestimated most.

In PHP, the model is shared-nothing per request. A request comes in, the framework boots, you handle it, the process tears everything down, and the next request starts from a clean slate. Memory leaks barely matter — the worst case is one request. Global state is reset for you. You almost never think about two requests touching the same variable, because in the classic PHP model they physically can't; they're separate processes 🙂

Go is the opposite. The process is long-lived. One Go binary stays up and handles thousands of requests concurrently, in the same memory, often literally at the same time across goroutines. The moment I internalized that, a whole category of bugs I'd never had to think about became something I had to actively watch for:

  • A package-level variable is now shared across every concurrent request. In PHP that was a per-request convenience. In Go it's a data race waiting to happen.
  • A map written to by two requests at once will crash the whole process — not the one request, the whole binary. The PHP blast radius of "one bad request" doesn't exist; a panic from a concurrent map write can take down everything in flight 😢
  • Resources you open have to be closed deliberately, because nothing is tearing the world down after each request to clean up after you.

None of those are exotic. They're the baseline things you now have to keep in your head on every change, because the language and the runtime won't reset the world for you between requests the way PHP did. The point isn't a specific disaster — it's that a whole class of failure that was simply impossible in the per-request PHP model is now possible by default, and avoiding it is on you 🧐

Rewiring this meant changing my default question. In PHP I asked "what does this request need?" In Go I had to ask "what happens when a thousand of these run at once, in the same memory?" That question is now automatic, but it took real production exposure to make it automatic.


3. Concurrency is in the language, so you own correctness

PHP's concurrency story, for most of my career, was "use a queue and more workers." Parallelism lived outside the language — in the infrastructure, in separate processes, in something like a job queue. I rarely reasoned about two things touching the same data in the same memory, for the same reason as above: they usually couldn't.

Go puts concurrency in my hands directly. go someFunc() starts a goroutine 👍. Channels pass data between them 👍. It's genuinely powerful, and it's the reason Go fit the kind of services we were building 🥳. But the power comes with ownership: the language hands you concurrency and then holds you responsible for correctness. A goroutine that writes to a shared structure without coordination is a bug that may pass every test on your machine and only surface under real load 🥲.

Two specific habits I had to build that PHP never required:

  • Reach for the race detector early. In my own projects I run tests with -race, and it's caught real races a few times now — races I'd never have spotted by reading the code, because they only show up when goroutines happen to interleave the wrong way at run time 🙏. In PHP I never had a tool like that, because I never had the problem.
  • Decide deliberately how goroutines share data — pass copies, use a channel, or protect shared state with a mutex — instead of just sharing a variable because it's in scope. "It's in scope so I'll use it" is a perfectly safe PHP habit and a dangerous Go one ☝️

The mental shift: in PHP, concurrency was an infrastructure concern I delegated. In Go, it's a code concern I own line by line.


4. context.Context is the spine, not a parameter you tolerate

When I first saw ctx context.Context as the first argument of seemingly every function, I treated it the way I'd treated similar things in PHP frameworks — boilerplate to thread through and otherwise ignore. That was wrong, and it cost me before it clicked 😅.

In PHP, the request was bounded for me. When the client disconnected or the request finished, the process ended; I never had to manually propagate "this work should stop now." In a long-lived Go service, nothing stops your work automatically. If a client gives up, or a request times out, the goroutines doing the work for that request will happily keep running — querying the database, calling other services — for no one. context.Context is how cancellation and deadlines travel down through every call so that work can actually be stopped and resources released.

Once I understood it as the cancellation and deadline spine of the request, passing ctx everywhere stopped feeling like boilerplate and started feeling like the thing that keeps a long-lived service from leaking work 🙌. In a marketplace with a lot of concurrent traffic, "stop doing work nobody is waiting for" is not a nicety; it's how the service stays healthy under load.

A concrete way I use it: when I call another service, I put a deadline on the context — lets say around 5 seconds. If that downstream call runs past the deadline, the context fires, I stop waiting, and I return a clean degraded response to the client — something like "we can't process this right now, please try again in a few minutes" 🤔 — instead of leaving the request hanging forever and tying up resources behind it. That's the whole point of the spine: the deadline travels down with the call, and when it expires everyone downstream can give up together. Under load, failing fast and politely is far better than hanging, and context.Context is what makes that possible without threading a timeout flag through every function by hand 👌


5. Composition over inheritance — and Laravel had hidden how much I leaned on inheritance

This one was subtle because I didn't realize how much of my PHP design instinct was inheritance-shaped until Go took the option away 😎

In Laravel, so much is built on extending base classes — your controllers, your models, your form requests all inherit a large amount of behavior from the framework. The "right" way to add capability was often "extend the base class." That instinct is invisible while you have it; it just feels like how code is organized.

Go has no class inheritance (in usual meaning). It has struct embedding and, more importantly, interfaces that are satisfied implicitly — a type implements an interface just by having the right methods, with no implements keyword and no declared relationship. Coming from PHP's explicit class Foo extends Bar implements Baz, implicit interfaces felt almost too loose at first. Where's the contract? Who guarantees it? 🤯

What rewired it for me was using this pattern consistently at the marketplace-project. There, I define interfaces at the consumer for repositories, for internal services, and for external services — a small interface declared where it's used, and any type with the right methods satisfies it. The consumer declares only the handful of methods it actually needs; the provider doesn't have to know the interface exists. That's a very different shape from "everyone extends the framework's base class," and it produces code where dependencies are small and swapping an implementation is trivial — a real database behind a repository in production, a fake satisfying the same interface in a test. Writing tests stopped requiring a whole framework's worth of scaffolding and started being a matter of passing in a small fake that fits the interface 🥳

Coming to that from years of Laravel's extends-everything instinct is exactly why I can feel how different it is. A developer who only ever wrote Go might take implicit interfaces for granted; I had to consciously give up the inheritance reflex to get there.


6. Explicit beats clever, and the language enforces it

PHP let me be clever. Dynamic typing, magic methods, arrays that were lists and maps and objects depending on my mood — a lot of expressiveness, and a lot of rope. I wrote some clever PHP I was proud of and later could not fully reconstruct why it worked 😅

Go is deliberately boring in a way that annoyed me at first and that I now value. No magic methods. No implicit type juggling. The compiler refuses unused variables and unused imports. The formatting is not up for debate — gofmt decides, and the entire community's code looks the same. Coming from PHP's freedom, this felt like the language not trusting me 🤔

The reframe: Go optimizes for the code being read, not the code being written. On a team — and I was leading one through the rebuild, plus interviewing developers now — that tradeoff is obviously correct. Clever PHP is a liability the moment someone other than its author has to maintain it. Boring, explicit, uniformly-formatted Go is something a teammate can read at 2am during an incident and actually understand. The language took away cleverness, and what I got back - a code that my team could understand about the same way I do 😎

That, more than any single feature, is the mental model I'd most want a PHP developer to adopt before they write a line of Go: you are not writing for the interpreter's flexibility anymore. You are writing for the next person who reads it, and the language is going to hold you to that whether you like it or not 🧐


What I'd actually tell someone making this move

The syntax will take you a week. The mental models took me months, because the hard part isn't learning Go — it's unlearning the PHP assumptions that are so deep you don't know they're assumptions.

None of this is a complaint about PHP. Seven years of PHP is exactly what made the Laravel-to-Go migration something I could lead rather than just attend — I understood the system we were leaving as deeply as the one we were building. But the move only worked once I stopped writing Go in PHP's syntax and started actually thinking in Go.