今日已更新 163 条资讯 | 累计 40937 条内容
关于我们

标签:#back

找到 280 篇相关文章

AI 资讯

GitHub Actions adds a background marker, and the linear job stops being the only shape

A small word that changes the rhythm of a job For as long as I have been writing Actions workflows I have been carrying a quiet workaround in my head. Want to warm a cache while the build runs? Append & to the shell command, then squint at logs that arrive out of order and pray the job doesn't exit on you. It worked, sort of. It also meant that anything more interesting than "run one thing, then the next thing" lived as folklore, hidden inside run: blocks. GitHub closed that gap this week. On June 25 the Actions changelog announced that steps inside a job can now run concurrently, marked with a new background keyword and supported by helpers to wait for them and cancel them. Until now, the changelog notes, every step in a workflow ran in sequence, with each step starting only after the previous one completed. That single rule has shaped every workflow I have ever written. It is gone, and the replacement is the kind of feature you don't notice until the day you reach for it and it's there. What the keywords actually do There are four pieces, all of them documented in the announcement. background: true is the entry point. Set it on a step and that step starts running, and the next step starts immediately. It does not block the job. wait and wait-all are the rendezvous. wait pins on one or more named background steps and pauses until they finish. wait-all is the same idea against every background step still in flight. Either way you get back into a linear flow on your terms. cancel is the cleanup. It gracefully terminates a background step when you no longer need it, which is the missing piece if you have ever tried to kill a long-running side process from inside a job and ended up shelling out to kill . parallel is the convenience wrapper. The changelog describes it as taking a group of steps and converting them into background steps with a wait placed after. For the common "fan out, then join" shape, you write one block instead of decorating five steps by hand. Where

2026-06-26 原文 →
AI 资讯

Why I Still Believe in Zero-Cost BFF Layers After 6 Months (And What Broke)

Why I Still Believe in Zero-Cost BFF Layers After 6 Months (And What Broke) Honestly, I didn't expect to be writing this article. Six months ago, I built capa-bff — a zero-cost BFF framework that won a hackathon gold medal — and I thought I had it all figured out. "This is perfect," I told myself. "Zero configuration, works with any Spring Boot app, solves all the frontend aggregation problems." Spoiler alert: It didn't. Don't get me wrong — it's still great for what it is. But here's the thing about building developer tools: the real world has a way of humbling you. Let me walk you through what I learned, what works, what doesn't, and who should actually use this thing. What Even Is a BFF Anyway? If you're new to the term, BFF stands for Backend For Frontend . It's that intermediate layer between your frontend clients (web, mobile, mini-programs) and your backend services. The idea is simple: instead of making the frontend stitch together data from multiple backend APIs, you have this middle layer that does it for you. ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ Frontend │ -> │ BFF │ -> │ Backend │ │ (Web/Mobile)│ │ Aggregation │ │ Services │ └─────────────┘ └─────────────┘ └─────────────┘ The benefits are clear: Fewer network calls from the client Customized responses for each client type Better caching opportunities One place to handle auth/transformations But here's the catch most articles don't tell you: adding a BFF layer means another service to maintain , another deployment , another thing that can break . For small teams and startups, that cost can feel too high. That's exactly why I built capa-bff: I wanted a zero-cost BFF layer that you can just drop into your existing Spring Boot app. No new service, no extra deployment — just add the dependency and start aggregating APIs. How It Actually Works (Code Example) Let me show you the basics. With capa-bff, you define your aggregation in a simple annotation: @BffRoute ( path = "/user-dashboard" ) public

2026-06-25 原文 →
产品设计

HLD Fundamentas #7: Back-of-the-Envelope Calculations

When designing systems like Facebook, WhatsApp, Netflix, Amazon, or Instagram, one of the first questions a system designer asks is: Can a single server handle the traffic? How much storage will be needed? Do we need caching? How much RAM should our cache have? How many servers should we deploy? Before discussing databases, load balancers, microservices, or caching layers, we need a rough understanding of the scale. This is where Back-of-the-Envelope Calculations come into the picture. Why Do We Need Back-of-the-Envelope Calculations? Imagine you're asked to design Facebook. If you immediately start drawing: Load Balancer ↓ Application Servers ↓ Redis Cache ↓ Database without knowing the expected traffic, you're designing blindly. System design is fundamentally about making trade-offs. To make those trade-offs, we first need estimates. Back-of-the-envelope calculations help us answer: How much traffic will the system receive? How much data will be generated? How much cache memory is required? How many servers are needed? The numbers don't need to be perfect. They only need to be close enough to make architectural decisions. What Exactly Is a Back-of-the-Envelope Calculation? A quick estimation technique used to approximate: Traffic Storage Memory Server Capacity using rough assumptions. Think of it as: "Getting the order of magnitude correct rather than getting the exact number correct." A system designer rarely needs perfect accuracy during interviews. They need reasonable estimates. The Standard Estimation Flow Whenever you get a System Design question: Users ↓ Traffic ↓ Storage ↓ RAM / Cache ↓ Number of Servers ↓ Architecture Design Always estimate first. Design later. The Ultimate Estimation Cheat Sheet Storage Units Unit Value 1 KB 10³ Bytes 1 MB 10⁶ Bytes 1 GB 10⁹ Bytes 1 TB 10¹² Bytes 1 PB 10¹⁵ Bytes Time Units Unit Value 1 Minute 60 Seconds 1 Hour 3600 Seconds 1 Day 86,400 Seconds Common Assumptions Metric Approximation Peak Traffic 3× Average Traffic Active

2026-06-24 原文 →
AI 资讯

What Developers Underestimate About Long-Running Workflows

Long-running workflows look simple when you first build them. Something happens. A few systems exchange data. Everything completes. Done. At least that's the expectation. Reality is very different. The biggest thing I underestimated was time. Not execution time. Elapsed time. Because once workflows start running for hours, days, or continuously, strange things start happening. APIs become temporarily unavailable Data changes halfway through the process Retries arrive much later than expected Someone manually updates a record Another system processes things in a different order Nothing is broken. But everything is slightly different from when the workflow started. Early on, I assumed workflows were transactions. Start. Execute. Finish. Now I think of them as conversations between systems. And conversations can get interrupted. Another thing I underestimated: State changes. You might start processing an order that is "pending". Ten minutes later, another system marks it as "cancelled". An hour later, a retry comes in from an earlier step. If your workflow only thinks about data, weird things happen. Because the world has changed while the process was still running. Long-running workflows also expose assumptions you didn't know you made. Like: this API will always respond quickly data will arrive in order users won't modify records manually retries will happen immediately Those assumptions survive in testing. Production removes them quickly. One thing that changed how I build these systems: I stopped asking: "Will this workflow finish?" And started asking: "What state will the world be in when it finishes?" Because those are two very different questions. Most problems in long-running systems aren't caused by one big failure. They're caused by lots of small changes happening while the workflow is still alive. And if you don't account for that, eventually the workflow finishes successfully and still produces the wrong outcome. This is something we think about constantly

2026-06-24 原文 →
AI 资讯

MongoDB Indexes Finally Clicked for Me: Understanding Indexes, Compound Indexes & the Prefix Rule 🚀

While working on a MERN project, I came across these indexes: transactionSchema . index ({ user : 1 , date : - 1 }); transactionSchema . index ({ user : 1 , type : - 1 }); transactionSchema . index ({ user : 1 , category : - 1 }); My first reaction was: "Why are we creating 3 different indexes for the same schema? Isn't one index enough?" At that time, my understanding was: "Indexes help MongoDB find records faster." Which is true, but it wasn't enough to explain why multiple indexes existed for the same collection. That simple doubt led me down a rabbit hole of learning about indexes, compound indexes, how MongoDB stores them, and the famous Prefix Rule. Here's what I learned. What is an Index? Imagine a collection with millions of transactions. db . transactions . find ({ user : " Aarthi " }); Without an index, MongoDB may need to inspect every document until it finds the matching records. This is called a Collection Scan . Think of it like searching for a chapter in a book without a table of contents. You'd have to flip through page after page until you find it. An index works like a book's table of contents. Instead of scanning every document, MongoDB can jump directly to the relevant records. Example: db . transactions . createIndex ({ user : 1 }); Now MongoDB can quickly locate all transactions belonging to a specific user. What is a Compound Index? A compound index contains multiple fields. Example: db . transactions . createIndex ({ user : 1 , date : - 1 }); This means MongoDB organizes the index by: user └── date Conceptually, it looks something like: Aarthi 2025-08-10 2025-08-09 2025-08-08 John 2025-08-10 2025-08-05 The data is first grouped by user , and within each user, it is ordered by date . Now queries like: db . transactions . find ({ user : " Aarthi " }). sort ({ date : - 1 }); become very efficient. MongoDB can jump directly to Aarthi's records and retrieve them in date order. The Prefix Rule: The Concept That Finally Made It Click Consider this i

2026-06-24 原文 →
AI 资讯

# Unit of Work: Managing Database Transactions Like a Pro with Python

Introduction Every serious backend developer eventually faces the same problem: you need to make multiple changes to a database as part of a single business operation, and you need all of them to succeed or none of them to go through. Partial updates are worse than no updates at all - they leave your data in an inconsistent state that can be nearly impossible to debug in production. This is not a new problem. Enterprise developers have been solving it for decades, and Martin Fowler documented the canonical solution in his 2002 book Patterns of Enterprise Application Architecture : the Unit of Work pattern. In this article we are going to go deep on what Unit of Work is, why it exists, how it works internally, and how to build a clean, production-quality implementation from scratch in Python using only the standard library. By the end you will have a working implementation you can adapt to any project, and a solid understanding of how popular frameworks like SQLAlchemy and Django ORM implement this pattern under the hood. The full source code is available on GitHub: 👉 github.com/diegocastillo12/unit-of-work-python - ## Background: What is the Unit of Work Pattern? The Unit of Work pattern is part of Martin Fowler's catalog of Patterns of Enterprise Application Architecture (PoEAA), a collection of battle-tested solutions for common problems in enterprise software design. Fowler defines it as follows: > "A Unit of Work maintains a list of objects affected by a business transaction and coordinates the writing out of changes and the resolution of concurrency problems." Let's unpack that definition carefully. "Maintains a list of objects affected by a business transaction" - this means the Unit of Work acts as a tracker. When your business logic creates a new object, modifies an existing one, or marks one for deletion, it does not immediately write to the database. Instead, it registers the change with the Unit of Work, which keeps an in-memory list of everything that ne

2026-06-24 原文 →
AI 资讯

Electric air taxis are stuck in the courtroom

This is The Stepback, a weekly newsletter breaking down one essential story from the tech world. For more on aviation, air taxis, and Wi-Fi speeds at 30,000 feet, follow Andrew J. Hawkins. The Stepback arrives in our subscribers' inboxes on Sunday at 8AM ET. Opt in for The Stepback here. How it started Last year, […]

2026-06-21 原文 →
AI 资讯

Error Handling — Learning to Love `if err != nil`

Error Handling — Learning to Love if err != nil In part 3 I covered goroutines and channels, and how Go's concurrency model sidesteps a lot of the ceremony I was used to from the JVM. This time I'm tackling the thing I complained about in part 1 of this series before I'd even really tried it: error handling. I called if err != nil repetitive back then. A few weeks and a lot of real code later, I owe Go a partial apology. No Exceptions, On Purpose Coming from Java, the absence of try / catch is the first thing that feels like a missing feature. It isn't — it's a deliberate design choice. In Go, errors are just values. A function that can fail returns an error as its last return value, and the caller decides what to do with it, right there, inline: func divide ( a , b float64 ) ( float64 , error ) { if b == 0 { return 0 , errors . New ( "division by zero" ) } return a / b , nil } func main () { result , err := divide ( 10 , 0 ) if err != nil { fmt . Println ( "error:" , err ) return } fmt . Println ( "result:" , result ) } That's the pattern you'll write hundreds of times in Go: call a function, check err , handle it or bail out, move on. No hidden control flow jumping up the call stack to whichever catch block happens to match. No checked-exception signatures cluttering method declarations. No RuntimeException quietly skipping past five layers of code that had no idea it could happen. Whatever can fail is sitting right there in the function signature, and you're forced to look at it. Why the Repetition Is the Point My part 1 complaint was that if err != nil everywhere feels manual. It is manual — and that's exactly the trade Go is making. In Java, an exception thrown deep in a call stack can silently propagate through layers of code that never declared they might fail, and you only find out where things actually break by reading a stack trace after the fact. In Go, every single point where something can go wrong is visible in the source, in order, as you read top to

2026-06-21 原文 →
产品设计

Why UPI and Fintech Apps Need Business Logic Testing (Not Just Security Testing)

Most fintech breaches you read about involve a hacker, a vulnerability, and a headline. Most fintech losses I've actually seen up close involve none of those things. They involve someone who read the terms of a cashback offer more carefully than the product team did, found the one path through the workflow nobody had tested, and quietly walked away with money the system handed over willingly. That's the part standard security testing misses. A penetration test asks: can someone break in? Business logic testing asks a more uncomfortable question: what happens if someone uses every feature exactly as designed, just not exactly as intended ? In a country processing billions of UPI transactions a month, that second question matters just as much as the first — arguably more, because nobody needs a zero-day to abuse a referral program. Here's where that gap shows up most often in Indian fintech apps. Wallet Systems: Built for Speed, Tested for Function, Rarely Tested for Abuse A digital wallet sits at the intersection of multiple money-in paths — UPI, card, net banking, cashback credits — and at least one money-out path. Every intersection like that is a place where timing and assumptions can quietly fall apart. The classic version of this is a race condition: top up the wallet and spend from it in two near-simultaneous requests, and check whether the balance check happens before or after both transactions are committed. Done right, this should be impossible. Done wrong, a user can spend money that, technically, hadn't arrived yet — or spend the same balance twice. There's a quieter version of the same problem around refunds. If a refund is credited back to the wallet on a different timeline than the original debit was finalized, there's often a window where the balance briefly shows more than it should, and a fast enough user can act inside that window before reconciliation catches up. And then there's KYC tiering. Minimum-KYC wallets in India are deliberately capped at

2026-06-21 原文 →
AI 资讯

Why Modular Architecture Makes SaaS Platforms Easier to Scale

As SaaS platforms grow, the codebase becomes harder to maintain. Features expand, integrations multiply, and the system starts to feel tightly coupled. Modular architecture solves this problem by splitting the platform into independent, self‑contained components that evolve without breaking each other. What modular architecture means A modular system is built from isolated components that communicate through well‑defined interfaces. Each module has: its own logic, its own data boundaries, its own responsibilities, minimal knowledge about other modules. This separation reduces complexity and makes the platform easier to extend. Benefits of modular design A modular architecture provides several advantages: Independent development: teams can work on different modules without conflicts. Faster deployments: small modules deploy quickly and safely. Better testability: each module can be tested in isolation. Improved reliability: failures are contained within a single module. Easier scaling: only the modules under load need more resources. This approach is especially useful for platforms that integrate with multiple external APIs. Real‑world example Modern property management systems often use modular design to separate booking logic, pricing engines, messaging workflows, and synchronization services. A good example is an API‑driven rental operations automation system , where each module handles a specific part of the workflow and communicates through events. If you want to explore how a real SaaS platform structures its modules, you can check PMS.Rent . Conclusion Modular architecture is not just a design choice — it is a long‑term strategy for building scalable, maintainable, and reliable SaaS platforms. When each module is independent and well‑defined, the entire system becomes easier to evolve and operate.

2026-06-21 原文 →
AI 资讯

Handling Webhooks Safely and Reliably in SaaS Platforms

Webhooks are one of the most common ways SaaS platforms communicate with external services. They deliver real‑time updates about bookings, payments, messages, or status changes. But webhooks are also one of the most fragile integration points — and if they are not handled correctly, the entire system becomes unreliable. Why webhook handling is tricky Webhooks are inherently unpredictable because they depend on external systems. Common issues include: duplicate deliveries, missing events, delayed notifications, invalid payloads, unexpected retries, out‑of‑order events. A robust webhook handler must be prepared for all of these scenarios. Core principles of safe webhook processing A reliable webhook system follows several essential rules: Idempotency: every event must be safe to process multiple times. Signature validation: verify that the request is authentic. Payload schema validation: reject malformed data early. Queue‑based processing: never process webhooks synchronously. Retry logic: handle temporary failures gracefully. Audit logging: store every event for debugging and recovery. These principles ensure that even if the external service misbehaves, your platform remains stable. Real‑world example Modern property management systems depend heavily on webhooks for booking updates, cancellations, pricing changes, and guest messages. An example of a resilient webhook workflow can be seen in an event‑driven short‑term rental automation platform , where each webhook is validated, queued, processed idempotently, and logged for traceability. If you want to explore how a real SaaS platform structures webhook handling, you can check PMS.Rent . Conclusion Webhooks are powerful but unreliable by nature. A safe webhook handler must assume that events will arrive late, arrive twice, or arrive broken. When the system is designed with idempotency, validation, queues, and retries, webhooks become a reliable foundation for real‑time automation.

2026-06-21 原文 →
AI 资讯

Day 50 of Learning MERN Stack

Hello Dev Community! 👋 It is officially Day 50 — a massive half-century milestone on my daily, unbroken streak toward mastering full-stack MERN engineering! Reaching Day 50 feels absolutely incredible. Yesterday, I mapped out dynamic path parameters. Today, I wired the input engine by building a complete asset workflow: Capturing Host "Add New Product" data payloads and committing them to local file storage pipelines! Following Prashant Sir's backend sequence , today was all about bridging the gap between host client forms and backend architecture using the Model-View-Controller framework. 🧠 Key Learnings From Day 50 (Product Ingestion & Storage) Processing data mutations sent from input forms requires tight coordination between parsing middlewares and file serialization engines. Here is how I structured the logic today: 1. Intercepting Form Submissions ( POST /host/add-product ) Set up a clean route mapping inside hostRouter.js to process dynamic data blocks sent by the host. The endpoint parses input parameters securely via backend streams. 2. Utilizing Class Instances for Storage Instead of directly pushing raw unstructured dictionaries into file records, I initialized a new object instance using my Day 48 structural class framework ( new houseList(...) ). This forces incoming data attributes—like name, price, location, and images—to match my exact system layout blueprint. 3. Asynchronous File Serialization Invoked the instance method .save() , which runs a non-blocking background task: it reads the active database layout array inside homesdata.json , appends the newly formulated object safely, and flushes the stringified update back onto the hard drive array using Node's fs operations. javascript // A conceptual look at how my controller hands data over to the model layer today const Product = require("../model/home"); exports.postAddProduct = (req, res) => { const { title, price, location, rating, imageUrl } = req.body; // Instantiating the core class data mold

2026-06-20 原文 →
AI 资讯

Day 48 of Leaning MERN Stack

Hello Dev Community! 👋 It is officially Day 48 of my unbroken full-stack engineering journey! Yesterday, I refactored my modular core patterns into MVC architecture. Today, I linked up a major functional extension inside the /model layer by introducing JavaScript Classes (OOP) to coordinate my local file operations and storage data patterns! Instead of writing loose object definitions, I stepped up my enterprise game by structuring a reusable class footprint that encapsulates data parameters and handles non-blocking file-system persistence asynchronously. 🧠 Key Learnings From Day 48 (OOP Modeling & File Systems) As clearly shown in my development workspace layout within "Screenshot (116).png" , modeling data with dedicated classes shifts your core structural logic from simple scripts into highly scalable engines: 1. The Model Data Blueprinter ( constructor ) I used the standard ES6 class framework inside home.js to structure an explicit data mold ( houseList ) with attributes tracking: houseName , price , location , rating , and photoUrl . This ensures every entry traveling through our server follows an identical structure. 2. Streamlining Async Persistence ( save() ) Rather than relying on globally declared floating arrays, my .save() blueprint method triggers an internal lookup to read existing data stacks asynchronously before safely using fs.writeFile() to serialize and flash mutated JSON rows into a local data asset ( homesdata.json ). 3. Static Decoupled Fetchers ( static fetchAll() ) I mastered using static methods. Since reading a data grid requires pulling records without creating an instance of a single house first, making fetchAll(callback) static allows our controllers to tap the hard disk records straight from the class reference layout: javascript // A conceptual look at my file-reading design today static fetchAll(callback) { const filePath = path.join(rootDir, 'data', 'homesdata.json'); fs.readFile(filePath, (err, data) => { if (err) { callback(JSON.

2026-06-20 原文 →
AI 资讯

Enterprise Design Patterns in Python: Repository & Unit of Work — Real-World E-Commerce Example

Enterprise Design Patterns in Python: Repository & Unit of Work 🐍🏗️ Series: Enterprise Application Architecture | Source: Fowler's EAA Catalog | Code: GitHub Repository 🧠 What Are Enterprise Design Patterns? Martin Fowler's Patterns of Enterprise Application Architecture (2002) is one of the most influential books in software engineering. It documents recurring architectural solutions — patterns — that solve common problems in enterprise systems: how to organize domain logic, how to talk to databases, how to handle transactions, and more. In this article, we'll explore two of the most powerful and widely-used patterns from that catalog: Pattern Category Core Purpose Repository Data Source Abstracts data access behind a collection-like interface Unit of Work Data Source Tracks object changes and commits them as a single transaction These two patterns work beautifully together — and you'll see exactly why with a real-world example. 🛒 The Problem: An E-Commerce Order System Imagine you're building a backend for an online store. When a customer places an order: A new Order is created Each Product 's stock is decremented A Payment record is registered If any of these steps fail midway, the entire operation should roll back — no partial state. This is exactly the problem the Unit of Work pattern solves, and the Repository pattern makes it all cleanly testable. 📁 Repository Pattern Definition "A Repository mediates between the domain and data mapping layers using a collection-like interface for accessing domain objects." — Martin Fowler, PoEAA The Repository acts as an in-memory collection of domain objects. Your business logic never knows if it's talking to PostgreSQL, SQLite, or even a mock list — it just calls .add() , .get() , .list() . Domain Model # models.py from dataclasses import dataclass , field from typing import List from uuid import uuid4 @dataclass class Product : id : str name : str price : float stock : int @dataclass class OrderItem : product_id : str qua

2026-06-20 原文 →
AI 资讯

My API Responded in 4 ms, but Navigation Still Felt Slow

I was debugging an internal project management application built with SvelteKit and a Rust API. Locally, navigation felt almost instant. On the VPS, opening the Tickets, Timeline, and OpenSpec docs pages felt noticeably slower. Clicking a ticket also took too long before the preview panel became useful. My first assumption was infrastructure: Maybe the VPS was underpowered. Maybe PostgreSQL queries were slow. Maybe the reverse proxy added latency. Maybe SvelteKit SSR was taking too long. The measurements pointed somewhere else. The Baseline I started with the feature list endpoint used by both Tickets and Timeline. For a project with 52 tickets: Metric Result API response time ~4 ms Response size 353,956 bytes Number of tickets 52 The API was not slow. But it was returning around 354 KB for a list of only 52 items. The SvelteKit route payload showed the same pattern: Route Data payload Tickets 349,857 bytes Timeline 354,731 bytes This explained why local testing was misleading. On localhost, transferring and parsing a few hundred kilobytes is easy to miss. Once the app runs behind a VPS, reverse proxy, TLS, and a real network connection, the payload becomes much more visible. What Was Inside the Payload? I broke down the feature response by field. The descriptions alone accounted for: 296,177 bytes That was more than 80% of the complete response. The list endpoint was returning something similar to this for every ticket: interface FeatureListItem { id : string ; title : string ; status : string ; priority : string ; storyPoints : number | null ; dueDate : string | null ; description : string | null ; checkoutCommand : string | null ; openSpecCommand : string | null ; } The problem was not that these fields were useless. They were useful on the ticket detail panel. They were not useful when rendering the initial list. Timeline was even more wasteful. It used ticket status, dates, dependencies, and assignees, but still downloaded every full Markdown description. The D

2026-06-20 原文 →
AI 资讯

Why Retries Are More Dangerous Than Failures in Production Systems

Failures are obvious. Retries are sneaky. When something fails, everyone notices. An alert goes off. A request errors out. Someone starts investigating. Retries are different. They look harmless. Most of the time, they save the system. But sometimes, retries create bigger problems than the original failure. Imagine an API call times out. No problem. The system retries. But what if the first request actually succeeded and only the response was lost? Now the retry creates: duplicate orders repeated emails inconsistent records workflows running twice The failure happened once. The retry multiplied it. Another thing I've seen: One slow dependency causes requests to pile up. Retries start firing. Those retries create even more traffic. Which slows things down further. Which triggers even more retries. Suddenly, the system is spending more effort retrying than doing useful work. Retries also hide problems. A temporary issue gets retried five times and eventually succeeds. Everything looks normal. Meanwhile: latency increases queues grow users experience delays Nothing technically failed. But the system is getting less healthy. What changed for me is that I stopped treating retries as free. Every retry has a cost. It consumes resources. It increases load. And if actions aren't designed carefully, retries can repeat side effects that should only happen once. Now when I build something, I don't ask: "What happens if this fails?" I ask: "What happens if this runs again?" Because in production, things almost always run again. And if the answer is "bad things happen," the retry mechanism isn't helping. It's making things worse. Failures are part of every system. Retries are too. The difference is that failures usually happen once. Retries can turn one problem into hundreds if you don't design for them. This is something we think about constantly at BrainPack when operating long-running workflows across multiple systems. AI and automation layers make retries even more common, wh

2026-06-19 原文 →
AI 资讯

Go's Type System — Structs, Interfaces, and Life Without Inheritance

Go's Type System — Structs, Interfaces, and Life Without Inheritance In part 1 of this series I talked about why I'm picking up Go after six years of Java and Kotlin, plus a recent deep dive into Rust. This time I want to get into the part that actually changed how I think about designing code: Go has no class inheritance at all. Coming from the JVM world, that sentence sounded alarming the first time I read it. No extends . No abstract classes. No polymorphism through a class hierarchy. And yet Go backends at companies running serious scale seem to do just fine without it. After a few weeks living inside Go's type system, I get why. Structs: Data, Nothing More A Go struct is just a typed bag of fields. No constructors, no access modifiers in the Java sense, no inheritance: type Order struct { ID string Customer string Amount float64 Status string } func NewOrder ( id , customer string , amount float64 ) Order { return Order { ID : id , Customer : customer , Amount : amount , Status : "pending" , } } That NewOrder function is doing the job a constructor would do in Java — it's just a plain function by convention, not a language feature. Nothing stops you from building an Order{} directly with zero values either, which takes some adjusting to if you're used to constructors enforcing invariants. Methods attach to structs separately, outside the type definition: func ( o Order ) Total () float64 { return o . Amount } func ( o * Order ) MarkPaid () { o . Status = "paid" } That (o Order) vs (o *Order) distinction is the receiver type, and it trips up a lot of newcomers. A value receiver gets a copy of the struct; a pointer receiver can mutate the original. MarkPaid has to use a pointer receiver, or the status change would vanish the moment the method returns. No Inheritance, So What Replaces It? This is the part that took the most rewiring. In Java, if PremiumOrder needed everything Order had plus more, you'd write class PremiumOrder extends Order . Go simply doesn't hav

2026-06-19 原文 →
AI 资讯

Exploring Lore: A Scalable Open Source Version Control System

What was released / announced Lore is an open source version control system designed with scalability in mind, allowing developers to efficiently manage large-scale projects. According to the official website, Lore aims to provide a more efficient and scalable alternative to traditional version control systems. This release is particularly exciting for developers and engineers working on complex projects that require robust version control. Why it matters As someone who works with large-scale AI infrastructure and cloud systems, I can attest to the importance of reliable version control. With Lore, developers can expect improved performance and reduced latency when managing massive codebases. This is especially crucial in environments where multiple teams collaborate on the same project, and version control becomes a bottleneck. I believe Lore has the potential to streamline development workflows and enhance overall productivity. How to use it To get started with Lore, you can begin by installing the command-line tool using the following command: pip install lore Once installed, you can initialize a new Lore repository using: lore init Lore also provides a REST API for integrating with other tools and services. For example, you can use the following Python code snippet to interact with the Lore API: import requests response = requests . get ( ' https://your-lore-instance.com/api/repo ' ) print ( response . json ()) You can explore more API endpoints and usage examples in the official Lore documentation. My take As an AI infrastructure engineer and DevOps architect, I'm excited about the potential of Lore to improve our development workflows. In my experience, traditional version control systems often struggle with large-scale projects, leading to performance issues and frustration. Lore's focus on scalability and performance could be a game-changer for teams working on complex AI and machine learning projects. I'm looking forward to exploring Lore further and integr

2026-06-19 原文 →