Skip to content
Frontend Feeds
  • Today
  • Archive
  • Sources
  • Categories
    • The Giants6 Sources
    • Multi Author Blogs18 Sources
    • Top Front-end Bloggers25 Sources
    • More Front-end Bloggers77 Sources
    • Browsers, engines, etc.11 Sources
    • Libraries, Frameworks, etc.12 Sources
    • Company/Startup Blogs12 Sources
    • Developer/Designer News4 Sources
    • YouTube Channels13 Sources
    • Podcasts7 Sources

Today

Front-end, in one read.

The latest from the sources worth following, newest first.

Wednesday, 11 November 2026

  • SitePointThe Giantsread at source

    The Design Workflow Is Broken. Here Is How Vitalii Khomenko Thinks It Should Be Fixed

    Principal Designer Vitalii Khomenko argues the real bottleneck in AI-accelerated product teams isn't engineering anymore, it's the Figma-to-code handoff, and here's his fix. Continue reading The Design Workflow Is Broken. Here Is How Vitalii Khomenko Thinks It Should Be Fixed on SitePoint .

  • DEV CommunityThe Giantsread at source

    Currency API for AI Agents: Live Exchange Rates via Tool Calling

    Ask a language model what 2,500 US dollars is in euros and it will answer with confidence. The number will be wrong, because the model last saw a rate during training. The fix is a currency API exposed as a tool. The model decides when it needs a rate, your code fetches it, and the model reports what came back. This tutorial builds that with tool calling in Python, then shows how to skip the code with an MCP server. Why an agent must never guess a rate Training data is old. Rates move every minute. A model's figures are months out of date the day it ships. Wrong numbers look right. A model asked for a rate produces four plausible decimals. Nothing in the text tells a user it was invented. The cost is real. An agent that quotes prices, drafts invoices or answers finance questions passes the error straight to a customer. Arithmetic is weak. Even with a correct rate, multiplying in text is where models slip. Do the maths in code. So the rule for the agent is simple: no rate without a tool call, and every rate quoted with its timestamp. The tool schema One tool covers most questions. It takes a source currency, a target currency and an optional amount: { "name" : "get_exchange_rate" , "description" : "Get the live mid-market exchange rate between two currencies, refreshed every 60 seconds. Optionally convert an amount. Always use this tool for any exchange rate or conversion; never state a rate from memory." , "input_schema" : { "type" : "object" , "properties" : { "source" : { "type" : "string" , "description" : "ISO 4217 code to convert from, e.g. USD" } , "target" : { "type" : "string" , "description" : "ISO 4217 code to convert to, e.g. EUR" } , "amount" : { "type" : "number" , "description" : "Optional amount in the source currency" } } , "required" : [ "source" , "target" ] } } The description does real work. It tells the model when to call the tool and forbids the fallback you are trying to remove. The implementation The function calls GET /api/v1/rates with a Bearer key. The response is a JSON array even for one pair, so read [0] . The conversion happens here, not in the model: import os , re , requests ALLRATES_KEY = os . environ [ "ALLRATES_API_KEY" ] ISO = re . compile ( r "^[A-Z]{3}$" ) def get_exchange_rate ( source , target , amount = None ) : source , target = source . upper ( ) , target . upper ( ) if not ( ISO . match ( source ) and ISO . match ( target ) ) : return { "error" : "source and target must be 3-letter ISO 4217 codes" } r = requests . get ( "https://allratestoday.com/api/v1/rates" , params = { "source" : source , "target" : target } , headers = { "Authorization" : f "Bearer {ALLRATES_KEY}" } , timeout = 10 , ) if r . status_code == 401 : return { "error" : "the exchange rate service rejected the API key" } if r . status_code == 429 : return { "error" : "rate limit reached; try again later" } r . raise_for_status ( ) row = r . json ( ) [ 0 ] result = { "source" : row [ "source" ] , "target" : row [ "target" ] , "rate" : row [ "rate" ] , "rate_type" : "mid-market" , "as_of" : row [ "time" ] , } if amount is not None : result [ "amount" ] = amount result [ "converted" ] = round ( amount * row [ "rate" ] , 6 ) return result Errors come back as data, not exceptions. The model can then tell the user the rate is unavailable instead of inventing one. On the Medium plan and above you can pass &amount= to the same endpoint and let the API convert; the local multiplication above works on every plan. Wiring it into Claude with tool use With the Anthropic Messages API, you pass the tool list, run the function whenever the response stops with tool_use , and send the result back as a tool_result block. The loop ends when the model answers in text. import json , anthropic client = anthropic . Anthropic ( ) # reads ANTHROPIC_API_KEY TOOLS = [ { "name" : "get_exchange_rate" , "description" : ( "Get the live mid-market exchange rate between two currencies, " "refreshed every 60 seconds. Optionally convert an amount. Always use " "this tool for any exchange rate or conversion; never state a rate from memory." ) , "input_schema" : { "type" : "object" , "properties" : { "source" : { "type" : "string" , "description" : "ISO 4217 code to convert from, e.g. USD" } , "target" : { "type" : "string" , "description" : "ISO 4217 code to convert to, e.g. EUR" } , "amount" : { "type" : "number" , "description" : "Optional amount in the source currency" } , } , "required" : [ "source" , "target" ] , } , } ] SYSTEM = ( "You answer currency questions. Never state an exchange rate you did not get " "from get_exchange_rate. Quote the as_of time with every rate." ) def ask ( question ) : messages = [ { "role" : "user" , "content" : question } ] while True : resp = client . messages . create ( model = "claude-sonnet-5-5" , max_tokens = 1024 , system = SYSTEM , tools = TOOLS , messages = messages , ) if resp . stop_reason != "tool_use" : return "" . join ( b . text for b in resp . content if b . type == "text" ) messages . append ( { "role" : "assistant" , "content" : resp . content } ) results = [ ] for block in resp . content : if block . type == "tool_use" : out = get_exchange_rate ( ** block . input ) results . append ( { "type" : "tool_result" , "tool_use_id" : block . id , "content" : json . dumps ( out ) , "is_error" : "error" in out , } ) messages . append ( { "role" : "user" , "content" : results } ) print ( ask ( "How much is 2,500 USD in EUR and in JPY right now?" ) ) For that question the model usually makes two tool calls in one turn, one per target. The loop handles both and returns a single answer with both amounts and their timestamps. The same schema with other providers The input_schema object is ordinary JSON Schema. OpenAI's function calling takes the same object under parameters in a function definition, and Gemini takes it in a function declaration. The Python function stays exactly as it is. Only the code that reads tool calls from the response and sends results back changes, and every provider's SDK documents that loop. Production details Cache for 60 seconds. Rates refresh every 60 seconds, so a cache keyed on the pair with a 60-second TTL saves requests without serving anything staler than the source. See how to cache exchange rates . Many targets, one request. If your agent often needs a list, let the tool accept target=EUR,GBP,JPY . The API returns one array and charges one request. Keep the key server side. The model never sees it. It only sees the function's output. Label the rate. Return rate_type: "mid-market" so the model can say what kind of rate it is quoting. Mid-market differs from what a bank charges , and a good agent says so when someone asks about a transfer. Add an official-rate tool for finance questions A live market rate is the wrong answer to "what rate do I use for this VAT invoice". That question has an official answer: the table of the central bank or tax authority the rule names. A second tool handles it: def get_official_rate ( bank , source , target ) : r = requests . get ( f "https://allratestoday.com/api/v1/central-bank/{bank}/latest" , params = { "source" : source . upper ( ) , "target" : target . upper ( ) } , headers = { "Authorization" : f "Bearer {ALLRATES_KEY}" } , timeout = 10 , ) r . raise_for_status ( ) d = r . json ( ) return { "bank" : d [ "bank" ] , "rate" : d [ "rate" ] , "rate_date" : d [ "rate_date" ] , "rate_type" : "official" } Describe it as "the official rate issued by a named central bank, for invoices, tax and accounting", and the model will choose between the two tools by intent. The rate_date is the institution's own date, so the agent can cite it. Coverage is 123 central banks and 6 tax authorities; the central bank rates API lists the bank codes. Skip the code: use the MCP server If your agent runs in a client that supports the Model Context Protocol, such as Claude Desktop, Claude Code, Cursor or VS Code, you do not need to write the tool at all. The AllRatesToday MCP server exposes get_exchange_rate , get_historical_rates , get_official_rates , list_currencies and more. Use the hosted server, configured with your API key in the Authorization header: claude mcp add --transport http allratestoday https://allratestoday.com/api/mcp \ --header "Authorization: Bearer YOUR_API_KEY" Or run the npm package locally over stdio: claude mcp add allratestoday -e ALLRATES_API_KEY = YOUR_API_KEY -- npx -y @allratestoday/mcp-server Setup for Cursor, VS Code, Codex CLI and Gemini CLI is on the MCP page . Agent frameworks that speak MCP can attach the same hosted URL, which is a quick way to test a tool design before you write your own. Which route to pick Situation Use Your own backend calls an LLM API Function tool, as above You need custom rules, caching or logging around each call Function tool A desktop or IDE agent, or a framework with MCP support MCP server Several clients should share one setup Hosted MCP URL Either way, get a key on the register page , read the API docs , and if you are still comparing providers, start with the free currency API comparison . FAQ Can an LLM know today's exchange rate without a tool? No. A model only knows the rates in its training data, which are months old, and it will still produce a precise-looking number if asked. Give it a tool that calls a currency API and tell it never to state a rate it did not get from that tool. Should the model do the conversion arithmetic? Let the tool do it. Return the rate, the converted amount and the rate timestamp from your function, so the model only has to report numbers rather than multiply them. Does the same tool schema work with OpenAI or Gemini? Yes. The input is plain JSON Schema. Anthropic puts it under input_schema, OpenAI under parameters inside a function definition, and Gemini in a function declaration. Your Python implementation does not change. Do I need to write code to give an agent exchange rates? Not if your client supports MCP. Add the hosted server at https://allratestoday.com/api/mcp with your API key in the Authorization header, or run @allratestoday/mcp-server locally with ALLRATES_API_KEY set. The agent then gets the currency tools directly. Which rate should an agent quote for an invoice? An official one. A live mid-market rate is right for "what is this worth now". For an invoice, VAT or accounting question, give the agent a tool for the central bank or tax authority your rule names, and have it quote the rate_date that comes back. Originally published at allratestoday.com .

  • DEV CommunityThe Giantsread at source

    How to check record counts and parent relationships after a Salesforce load

    AI-Assisted By Richard C, AIL Studio Spreadsheet formulas were tested in LibreOffice Calc 25.2.3.2, not in Google Sheets, Excel, or a Salesforce org. No Salesforce org was queried to write this article. A finished Data Loader or Bulk API job can still be short on rows, or it can accept every child while some parent lookups stay blank and one parent ends up with fewer children than the source file. This walkthrough reconciles source-to-target counts per object using the job’s own success and error files, then checks whether parents resolved: how many children have a blank parent lookup, and whether each parent has the same number of children in the source and in the target. It does not check whether a weekly Data Export downloaded every zip or attachment before you migrated. It does not walk through Contact-export orphans, duplicate External IDs, or picklist mismatches; those are separate file checks. 1. Count what the job accepted, then what the org contains After an import, Data Loader generates two CSV output files. “One file name begins with success , and the other starts with error .” The success file “contains the successfully loaded records, including a column with the newly generated record IDs.” The error file “contains the rejected records, with a column that describes why the load failed.” If you loaded through the Bulk API, the job’s JobInfo includes numberRecordsProcessed (“the number of records already processed”) and numberRecordsFailed (“the number of records that weren’t processed successfully in this job”). Read those two fields from the completed job; do not invent a third number the API did not return. Then count the object in the target org with SOQL COUNT() . COUNT() “returns the number of rows that match the filtering conditions” and “must be the only element in the SELECT list”: SELECT COUNT ( ) FROM Contact Run the query as a user who can see the same records you meant to load. COUNT() “includes null values that match the filtering conditions,” which matters in the next section when you filter on a blank parent. For each object, put Source rows, Success rows, Error rows, Success+Error, Vs source, Target COUNT() , and Target vs success on a Counts sheet. In E2, F2 and H2, then fill down: =IF(OR(B2="",C2="",D2=""),"",C2+D2) =IF(E2="","",E2-B2) =IF(OR(C2="",G2=""),"",G2-C2) Example data (not a customer org, and not the result of a real load). Four Contact rows were sent. Three landed in the success file and one in the error file. The target COUNT() is 3. Object Source rows Success rows Error rows Success+Error Vs source Target COUNT() Target vs success Contact 4 3 1 4 0 3 0 In LibreOffice Calc 25.2.3.2 those formulas produced Success+Error 4 , Vs source 0 , and Target vs success 0 . A blank object row left the calculated cells blank. Matching totals here only mean the rejected row is accounted for in the error file. They do not mean every parent received the children you expected. 2. Did the parents resolve? Children point at parents through a lookup or a master-detail field. Considerations for Relationships (Salesforce Help) states that you can convert a lookup to a master-detail relationship “if the lookup field in all the records contains a value,” and that you cannot create a master-detail relationship if the custom object already contains data unless you first fill that parent field. After a load, treat a child that arrived with a blank parent as a lookup left empty (or as a row that never arrived and should be in the error file)—not as proof that a required parent was filled. Count children whose parent lookup is blank: SELECT COUNT ( ) FROM Contact WHERE AccountId = null COUNT() includes rows that match the filter, so this returns how many Contacts have no Account. To inspect the rows, use a relationship query that reads the parent with the dot operator: SELECT Id , LastName , Account . Name FROM Contact WHERE AccountId = null Account.Name will be empty on those rows. Replace Contact / AccountId with the child object and parent field you actually loaded. This is only a blank-parent count. It does not tell you whether a filled parent Id points at the wrong company, and it is not a picklist or External ID check. 3. Compare child counts per parent A parent can “resolve” on every successful child and still be short if some of its children were rejected. Compare how many children each parent has in the source file versus the target. In the target org, GROUP BY with COUNT(fieldName) returns a count per parent. Salesforce documents COUNT(fieldName) with GROUP BY for summary reporting, and notes that GROUP BY without an aggregate returns distinct values “including null ”: SELECT AccountId , COUNT ( Id ) FROM Contact GROUP BY AccountId Salesforce record Ids usually change between the source org and the target org, so do not compare those Id values to the source file. Export (or keep) a parent external key on both sides—this example uses Parent_Legacy_Id__c —and count children per that key in a sheet. Example data (same made-up load as section 1). Source Contacts: two children of AC-001 , one child of AC-002 , and one child with a blank parent. The rejected row was the AC-002 child, so the target has two children of AC-001 , none of AC-002 , and one blank parent. On a Parents sheet, list each parent key (use (blank) as the label for empty parents). Put the source parent keys in J2:J5 and the target parent keys in K2:K4 . For a named parent in A2: =COUNTIF($J$2:$J$5,A2) =COUNTIF($K$2:$K$4,A2) =IF(OR(B2="",C2=""),"",C2-B2) =IF(A2="","",IF(C2-B2=0,"Match","Review")) For the (blank) row, count empty parent keys explicitly: =COUNTIF($J$2:$J$5,"") =COUNTIF($K$2:$K$4,"") In LibreOffice Calc 25.2.3.2 those formulas produced: Parent Source children Target children Difference Status AC-001 2 2 0 Match AC-002 1 0 -1 Review (blank) 1 1 0 Match AC-002 is the point: the object-level counts in section 1 matched, and one child loaded with a blank parent lookup, but this parent has no children in the target because its child is in the error file. Widen the COUNTIF ranges to your last data row. 4. What matching counts do not prove A matching per-parent count does not prove each child points at the intended parent. Two parents that each had two children can swap those children and still show Match . A blank parent lookup can be intentional on an optional lookup; treat it as a review list. Permissions matter: SOQL you cannot see, you cannot count. Before you share any export outside your team, for a review or anything else, send only the objects and columns the reviewer needs. Remove or blank personal fields you don't need, such as personal emails, phone numbers, home addresses and birthdates, and never include passwords or org logins. AIL Studio is not affiliated with or endorsed by Salesforce, Inc. Salesforce is a trademark of Salesforce, Inc. If you want a second pair of eyes on Contact and Account exports (CSV or Excel, up to 25,000 rows per file), there's a $19 Salesforce Export Check ; the product page explains what's included and how to send the files. It reviews relationship and quality checks on those two objects (blank Account, Account values that are not in the Account file, blank required columns you name, External ID counts and wrong-parent Contacts when you send the matching files). It does not log into your org, merge records, or load data, and it is not a Bulk API job review or a children-per-parent count for every object.

  • DEV CommunityThe Giantsread at source

    Why Server Hardware Still Matters in the Age of Cloud Computing

    Around 2015 the pitch was that the server cupboard was finished. Everything was going to the cloud, and anyone still buying physical kit was being sentimental about it. A decade later, plenty of companies did move most of their IT into someone else's data centre and are glad they did. A quieter number moved things back. Some never left in the first place. That is not a story about the cloud being oversold. It is that the decision turned out to be per workload rather than per company, and the hardware under the abstraction still has opinions about your latency and your invoice. The box is still there Every cloud service runs on physical servers. When you launch an instance you are renting a slice of a machine sitting in a rack in a warehouse, drawing power and needing cooling like anything else with fans in it. The provider handles the parts nobody enjoys, which is most of why people pay them, but the hardware underneath still sets how fast your application answers. That stops being an abstract point the moment your workload does not fit the menu. Instance families come in fixed shapes, with a memory-to-vCPU ratio somebody else chose. If you need a lot of RAM for a database and very little CPU, you buy the CPU anyway. If your software is licensed per core, the shape of the instance is suddenly a line item in a completely different budget. Own the machine and you write the spec: this much RAM, that RAID level, four network ports, local NVMe instead of a volume attached over the network. What the invoice looks like in year three For anything short-lived the cloud is hard to argue against. No upfront spend, no lead time, and you turn it off when the project ends. That is a genuinely different thing from what hardware offers, and pretending otherwise is how people end up with a rack they did not need. The arithmetic moves once something has been running continuously for a few years. The monthly charge does not stop, and it drifts upward on its own as data accumulates. A machine bought outright is one number you can put in a plan. Enterprise hardware runs five years or more, the refurbished market makes that cheaper than most people assume, and finance teams tend to prefer a depreciating asset over a bill that moves with usage. Then there is egress. Ingress is usually free and data leaving is priced per gigabyte, which is fine until the day you have to pull several terabytes out in a hurry and someone sees the invoice afterwards. It is the unglamorous kind of arithmetic nobody budgets for in advance, a bit like how counting business days turns out to be harder than it looks. Storage in a room you walk past does not have that particular edge. Where the data physically sits Some teams do not get a free choice. A law firm, a clinic or a local authority department may have explicit obligations about where records live and who can reach them. Being able to name the room, the rack and the person holding the key makes an audit substantially less interesting than trying to establish where a provider's backup replicas ended up. The neighbour you cannot see Shared hosts mean shared hardware, and a busy tenant on the same physical machine can slow you down. The usual name for it is the noisy neighbour problem, and the reason it is maddening is that your own metrics look fine. CPU usage is normal, the application is doing nothing unusual, and things are just slower on Tuesday afternoons for no reason you can point at. The signal, when you can see it at all, is steal time: the share of cycles the hypervisor took away while your vCPU was ready to run. $ vmstat 1 5 procs -----------memory---------- ---system-- ------cpu----- r b swpd free buff cache in cs us sy id wa st 2 0 0 412332 98244 1884120 2103 4188 31 4 59 0 6 3 0 0 411980 98244 1884140 2456 4902 34 5 54 0 7 That st column is the one to watch. On dedicated hardware it is zero and stays zero, which is less of a performance feature than a debugging one. You stop having to rule it out. Distance is not free either A point of sale terminal or a line controller talking to a server on the same switch answers in about a millisecond. The same conversation with a region a few hundred miles away costs tens of milliseconds, every round trip, forever. If the protocol is chatty, that multiplies into something people notice. And when the connection to the outside world drops, the machine in the next room keeps serving. Most real setups are both Nearly every sensible arrangement I have seen ends up mixed. Email, video calls and the public website belong in the cloud, because they scale on demand and the large providers keep them up better than a small team can. File storage, accounting databases, backups and anything wired to specialist equipment often earn their place on hardware you control. The useful habit is deciding workload by workload rather than picking a side and defending it at every planning meeting. In practice, moving one or two heavy, steady, boring systems onto your own iron is what trims the monthly bill, while everything spiky stays where it is. If you are weighing it up, price the configuration you would actually buy instead of arguing from vibes. Vendors list what a given budget gets you, and seeing the real numbers on rack and tower servers alongside twenty-four months of invoices for the same workload settles the question faster than any amount of opinion. Sometimes the answer is that the cloud was right all along, which is also worth knowing before the next renewal. Keeping it proportionate You do not have to like or dislike the cloud. It is an excellent tool for some things and a poor one for others, and the same is true of a machine humming away in a comms room. Hardware matters because it sets the ceiling on cost, speed and control no matter whose name is on the electricity bill. Teams that keep that in mind and judge each system on its own terms usually end up with infrastructure that costs less and pages them less often.

  • DEV CommunityThe Giantsread at source

    Exchange Rates API Data in a Database: Schema, Indexes and As-Of Queries

    Sooner or later, every app that uses an exchange rates API needs its own copy of the rates. Invoices must be reproducible next year. Reports must use the rate of the transaction date, not today's. And you do not want a page view to depend on a network call. This guide gives a schema that holds up: one table, the right keys, an as-of lookup, idempotent loading and a retention plan. The SQL is for PostgreSQL, with notes for MySQL and SQLite at the end. The table CREATE TABLE fx_rate ( base CHAR ( 3 ) NOT NULL , -- ISO 4217, e.g. 'USD' quote CHAR ( 3 ) NOT NULL , -- 1 base = rate quote rate NUMERIC ( 20 , 10 ) NOT NULL CHECK ( rate > 0 ) , rate_type TEXT NOT NULL CHECK ( rate_type IN ( 'mid' , 'official' ) ) , source TEXT NOT NULL , -- 'allratestoday', 'ecb', 'rbi', ... rate_date DATE NOT NULL , -- the date the rate applies to rate_time TIMESTAMPTZ , -- the provider's own timestamp, if any fetched_at TIMESTAMPTZ NOT NULL DEFAULT now ( ) , PRIMARY KEY ( base , quote , rate_type , source , rate_date ) ) ; CREATE INDEX fx_rate_date_idx ON fx_rate ( rate_date ) ; Why each column is there: base and quote fix the direction. A row USD, EUR, 0.9215 means 1 USD = 0.9215 EUR. Write that rule in a comment, because direction bugs are the most expensive kind. rate as NUMERIC(20,10) , never FLOAT or DOUBLE . Floating point turns 0.9215 into 0.92149999... and those errors show up as one-cent differences in totals. Ten decimal places covers small-unit currencies against strong ones, and ten integer digits covers rates such as USD/IRR or USD/VND. rate_type separates a live mid-market rate from an official central bank rate. They are different numbers with different purposes, and a report must never mix them by accident. source names who produced the rate. For official rates it is the institution, such as ecb . This is the column an auditor asks about. rate_date is the business date the rate applies to. rate_time is the provider's timestamp. fetched_at is when your system got it. Keep all three; they answer different questions. Primary key and indexes The primary key (base, quote, rate_type, source, rate_date) says: one rate per pair, per type, per source, per day. That is the right grain for invoices, accounting and reports. The column order is deliberate. Lookups filter on pair, type and source, then range on date, so the primary key index serves the as-of query directly. The extra index on rate_date is for retention jobs and for "all rates on a date" exports. If you also keep intraday snapshots, put them in a separate table keyed on rate_time instead of rate_date . Mixing grains in one table makes every query ask "which row of the day?". As-of lookups: the latest rate on or before a date Rates do not exist for every date. Official rates skip weekends and holidays, and your loader might miss a day. The standard question is "the latest rate on or before date D". For several currencies at once, DISTINCT ON is the idiomatic Postgres answer: SELECT DISTINCT ON ( quote ) quote , rate , rate_date FROM fx_rate WHERE base = 'USD' AND quote IN ( 'EUR' , 'GBP' , 'JPY' ) AND rate_type = 'mid' AND source = 'allratestoday' AND rate_date DATE '2026-11-08' ORDER BY quote , rate_date DESC ; To attach a rate to each row of another table, use LATERAL . This converts invoices into USD with the rate on or before each invoice date, and refuses rates more than seven days older than the invoice: SELECT i . id , i . amount , i . currency , r . rate , r . rate_date , CASE WHEN i . currency = 'USD' THEN i . amount ELSE round ( i . amount / r . rate , 2 ) END AS amount_usd FROM invoice i LEFT JOIN LATERAL ( SELECT rate , rate_date FROM fx_rate WHERE base = 'USD' AND quote = i . currency AND rate_type = 'mid' AND source = 'allratestoday' AND rate_date i . invoice_date AND rate_date >= i . invoice_date - 7 ORDER BY rate_date DESC LIMIT 1 ) r ON true ; Each lateral subquery is a single index probe on the primary key. LEFT JOIN ... ON true keeps invoices with no rate, so you see a NULL instead of silently losing rows. Since rate is EUR per USD, a EUR amount divided by the rate gives USD. Why not store inverses It is tempting to store both USD/EUR and EUR/USD . Do not: They can disagree. If one is loaded and the other is not, or both are rounded, a * b stops being exactly 1, and two reports give two answers. It doubles the rows and the load work for no new information. It hides direction bugs. With one direction stored, a query that uses the wrong direction returns nothing, which you notice. With both stored, it returns a plausible wrong number. Store one direction per source and compute the inverse when you need it: 1 / rate . With NUMERIC , the inverse is exact to the precision you ask for. Triangulation through USD or EUR You do not need rows for every pair either. With 160 currencies there are over 25,000 pairs. Store each currency against one base and derive the rest. EUR/GBP is USD/GBP divided by USD/EUR: SELECT a . rate_date , round ( b . rate / a . rate , 10 ) AS eur_gbp FROM fx_rate a JOIN fx_rate b USING ( base , rate_type , source , rate_date ) WHERE a . base = 'USD' AND a . quote = 'EUR' AND b . quote = 'GBP' AND a . rate_type = 'mid' AND a . source = 'allratestoday' ORDER BY a . rate_date DESC LIMIT 30 ; The important rule: triangulate only within one source and one date . Official rates come in each institution's own base. The ECB quotes against EUR, a central bank in India quotes against INR. Store them as given, with their own base and source = 'ecb' and so on, and never combine an ECB rate with another bank's rate into a cross rate. The result would belong to no one. The base currency conversion guide goes deeper on cross rates. Idempotent upserts with ON CONFLICT Loaders get re-run: a cron fires twice, a job retries after a timeout, someone backfills a week by hand. The insert must be safe to repeat: INSERT INTO fx_rate ( base , quote , rate , rate_type , source , rate_date , rate_time ) VALUES ( $1 , $2 , $3 , 'mid' , 'allratestoday' , $4 , $5 ) ON CONFLICT ( base , quote , rate_type , source , rate_date ) DO UPDATE SET rate = EXCLUDED . rate , rate_time = EXCLUDED . rate_time , fetched_at = now ( ) WHERE fx_rate . rate IS DISTINCT FROM EXCLUDED . rate ; Running it twice leaves one row. The WHERE clause skips writes when nothing changed, which keeps write volume and table bloat down. For official rates, consider DO NOTHING plus a log entry when a value differs, so a correction by the institution is visible rather than silently overwritten. Loading daily from the exchange rates API One request fetches every currency you need. The AllRatesToday rates endpoint takes a comma-separated target list and returns a JSON array; a multi-target call costs one request. # load_rates.py (pip install requests "psycopg[binary]") import json import os from datetime import datetime from decimal import Decimal import psycopg import requests TARGETS = [ "EUR" , "GBP" , "JPY" , "CAD" , "AUD" , "CHF" , "INR" , "CNY" ] UPSERT = """ INSERT INTO fx_rate (base, quote, rate, rate_type, source, rate_date, rate_time) VALUES (%s, %s, %s, 'mid', 'allratestoday', %s, %s) ON CONFLICT (base, quote, rate_type, source, rate_date) DO UPDATE SET rate = EXCLUDED.rate, rate_time = EXCLUDED.rate_time, fetched_at = now() WHERE fx_rate.rate IS DISTINCT FROM EXCLUDED.rate """ resp = requests . get ( "https://allratestoday.com/api/v1/rates" , params = { "source" : "USD" , "target" : "," . join ( TARGETS ) } , headers = { "Authorization" : f "Bearer {os.environ['ALLRATES_API_KEY']}" } , timeout = 15 , ) resp . raise_for_status ( ) rows = json . loads ( resp . text , parse_float = Decimal ) # keep exact decimals with psycopg . connect ( os . environ [ "DATABASE_URL" ] ) as conn : for r in rows : ts = datetime . fromisoformat ( r [ "time" ] . replace ( "Z" , "+00:00" ) ) conn . execute ( UPSERT , ( r [ "source" ] , r [ "target" ] , r [ "rate" ] , ts . date ( ) , ts ) ) parse_float=Decimal keeps the rate exact from JSON to NUMERIC ; going through a Python float would reintroduce rounding. Schedule the loader at a fixed time each day, for example just after your business close. If it runs twice the same day, the upsert keeps the later snapshot. Daily loading is about 30 requests a month, which fits any paid plan; commercial use starts on the Small plan, see pricing . For backfills, /api/historical-rates?source=USD&target=EUR&period=1y returns weekly points and costs 12 requests per pair. For daily official rates over a date range, the central bank history endpoint, such as /api/v1/central-bank/ecb/history?source=USD&target=EUR&from=2026-01-01&to=2026-03-31 , returns the institution's own dates and is billed per month of range. See the central bank rates API . Retention Daily rows are tiny. 160 currencies for one source is about 58,000 rows a year, a few megabytes with indexes. Keep daily rates forever; old invoices and audits need them. Intraday snapshots are different. One snapshot a minute for 160 currencies is about 230,000 rows a day. Partition that table by month and drop partitions you no longer need, keeping only the daily row in fx_rate long term. MySQL and SQLite notes Topic PostgreSQL MySQL 8 SQLite Rate column NUMERIC(20,10) DECIMAL(20,10) TEXT , parse in the app Upsert ON CONFLICT ... DO UPDATE ON DUPLICATE KEY UPDATE ON CONFLICT ... DO UPDATE (3.24+) As-of, many rows DISTINCT ON ROW_NUMBER() OVER (PARTITION BY quote ORDER BY rate_date DESC) ROW_NUMBER() (3.25+) As-of per row LATERAL LATERAL (8.0.14+) or correlated subquery correlated subquery Timestamps TIMESTAMPTZ DATETIME in UTC ISO 8601 TEXT in UTC SQLite deserves a warning: a column declared NUMERIC stores 0.9215 as a floating-point value. Store rates as text and convert with a decimal type in your application. If you are still choosing where the data comes from, the free currency API comparison lists update frequencies, history depth and limits. FAQ What data type should I use for exchange rates in SQL? Use NUMERIC or DECIMAL, for example NUMERIC(20,10). Never use FLOAT or DOUBLE, because binary floating point cannot represent most decimal rates exactly and the errors show up in totals. How do I get the exchange rate for a date with no rate? Use an as-of lookup: the latest rate on or before the date. In Postgres, use DISTINCT ON for many currencies or a LATERAL subquery with ORDER BY rate_date DESC LIMIT 1 per row, plus a maximum age. Should I store both USD/EUR and EUR/USD? No. Store one direction per source and compute the inverse as 1 divided by the rate. Storing both lets them drift apart. How do I avoid duplicate rows when the loader runs twice? Make the primary key the natural key (base, quote, rate type, source, rate date) and insert with ON CONFLICT DO UPDATE. Running the load again then updates or skips the row instead of adding one. Can I mix official and mid-market rates in one table? Yes, as long as rate_type and source are part of the key and every query filters on them. Never build a cross rate from two different sources. Originally published at allratestoday.com .

  • DEV CommunityThe Giantsread at source

    Exchange Rate API Alerts: Build a Currency Rate Alert in Python

    A currency rate alert is one of the most useful small things you can build on an exchange rate API. "Tell me when USD/EUR goes above 0.95." "Tell the finance channel when GBP/USD drops below 1.25." This guide builds one twice: as a Python script you can run from cron or GitHub Actions, and as a Cloudflare Worker with a Cron Trigger. Both send to Slack, Telegram or email, both remember what they already sent, and both use one API request per run. How the alert works Every run does five things: Fetch the latest rates for every currency in your rules, in one request. Work out which "zone" each rule is in: above the upper threshold, below the lower threshold, or inside. Compare with the zone stored from the previous run. Send a message only when a rule enters a new zone outside the band. Save the new zones. Step 3 is what stops the alert from firing every 15 minutes for as long as the rate stays above your line. Without stored state, a threshold check is a spam generator. The exchange rate API call Put every target currency in one call. The response is a JSON array, and a multi-target call costs one request. curl -H "Authorization: Bearer YOUR_API_KEY" \ "https://allratestoday.com/api/v1/rates?source=USD&target=EUR,GBP,JPY" [ { "rate" : 0.9215 , "source" : "USD" , "target" : "EUR" , "time" : "2026-04-03T12:00:00Z" } , { "rate" : 0.7893 , "source" : "USD" , "target" : "GBP" , "time" : "2026-04-03T12:00:00Z" } , { "rate" : 151.42 , "source" : "USD" , "target" : "JPY" , "time" : "2026-04-03T12:00:00Z" } ] Keep all rules on one base. If you want an alert on EUR/GBP, compute it from the same response: rate[GBP] / rate[EUR] . That way ten rules still cost one request per run. The rates are mid-market rates refreshed every 60 seconds. Request budget: frequency against plan quota Because each run is one request, the monthly cost is simply runs per month. For a 30-day month: Schedule Runs per 30 days Fits Every minute 43,200 Large (100,000/mo) Every 5 minutes 8,640 Medium (10,000/mo) Every 15 minutes 2,880 Small (5,000/mo) Hourly 720 Small (5,000/mo) Every 15 minutes is a good default for a threshold alert: 96 runs a day, 2,880 requests a month, which leaves room on the Small plan's 5,000 for other uses. A free API key is fine for building and testing the alert. The free allowance is a one-off evaluation allowance that does not reset, so a job that runs every day needs a paid plan; Small is €4.99 a month. Details are on the pricing page . Python version The script below reads rules, fetches rates, keeps state in a JSON file and sends notifications to whichever channels you configure. It needs only requests . # fx_alert.py (pip install requests) import json import os import smtplib from email . message import EmailMessage from pathlib import Path import requests API_KEY = os . environ [ "ALLRATES_API_KEY" ] STATE_FILE = Path ( os . environ . get ( "STATE_FILE" , "alert_state.json" ) ) # Pairs are always USD/ ; use "cross" for pairs computed from two USD rates. RULES = [ { "name" : "USD/EUR" , "target" : "EUR" , "above" : 0.95 } , { "name" : "USD/JPY" , "target" : "JPY" , "below" : 140.0 } , { "name" : "EUR/GBP" , "cross" : ( "EUR" , "GBP" ) , "above" : 0.88 , "below" : 0.82 } , ] def fetch_rates ( ) : targets = set ( ) for rule in RULES : targets . update ( rule . get ( "cross" , ( ) ) or [ rule [ "target" ] ] ) resp = requests . get ( "https://allratestoday.com/api/v1/rates" , params = { "source" : "USD" , "target" : "," . join ( sorted ( targets ) ) } , headers = { "Authorization" : f "Bearer {API_KEY}" } , timeout = 15 , ) resp . raise_for_status ( ) return { row [ "target" ] : row [ "rate" ] for row in resp . json ( ) } # array -> dict def value_for ( rule , rates ) : if "cross" in rule : base , quote = rule [ "cross" ] return rates [ quote ] / rates [ base ] return rates [ rule [ "target" ] ] def zone ( rule , value ) : if "above" in rule and value > rule [ "above" ] : return "above" if "below" in rule and value rule [ "below" ] : return "below" return "inside" def notify ( text ) : if url := os . environ . get ( "SLACK_WEBHOOK_URL" ) : requests . post ( url , json = { "text" : text } , timeout = 10 ) if token := os . environ . get ( "TELEGRAM_BOT_TOKEN" ) : requests . post ( f "https://api.telegram.org/bot{token}/sendMessage" , json = { "chat_id" : os . environ [ "TELEGRAM_CHAT_ID" ] , "text" : text } , timeout = 10 , ) if host := os . environ . get ( "SMTP_HOST" ) : msg = EmailMessage ( ) msg [ "Subject" ] = "Exchange rate alert" msg [ "From" ] = os . environ [ "ALERT_FROM" ] msg [ "To" ] = os . environ [ "ALERT_TO" ] msg . set_content ( text ) with smtplib . SMTP_SSL ( host , 465 ) as smtp : smtp . login ( os . environ [ "SMTP_USER" ] , os . environ [ "SMTP_PASSWORD" ] ) smtp . send_message ( msg ) def main ( ) : state = json . loads ( STATE_FILE . read_text ( ) ) if STATE_FILE . exists ( ) else { } rates = fetch_rates ( ) for rule in RULES : value = value_for ( rule , rates ) new = zone ( rule , value ) old = state . get ( rule [ "name" ] , "inside" ) if new != old and new != "inside" : limit = rule [ new ] notify ( f "{rule['name']} is {new} {limit}: now {value:.4f} (mid-market)" ) state [ rule [ "name" ] ] = new STATE_FILE . write_text ( json . dumps ( state ) ) if __name__ == "__main__" : main ( ) The state file maps each rule to its last zone. An alert fires when the zone changes to above or below . When the rate comes back inside the band, the zone resets to inside silently, which re-arms the alert. If a rate hovers right at the threshold, add a small buffer before re-arming, for example require it to come back 0.2% inside the line. Run it from cron on any server: */15 * * * * cd /opt/fx-alert && ALLRATES_API_KEY = YOUR_API_KEY /usr/bin/python3 fx_alert.py Running it on GitHub Actions GitHub Actions can run the same script on a schedule. The state file has to survive between runs, so the workflow restores it from the Actions cache and saves a new copy each time: name : fx-alert on : schedule : - cron : "*/15 * * * *" workflow_dispatch : jobs : check : runs-on : ubuntu-latest steps : - uses : actions/checkout@v4 - uses : actions/cache@v4 with : path : alert_state.json key : fx-alert-state-${{ github.run_id }} restore-keys : fx-alert-state- - run : pip install requests && python fx_alert.py env : ALLRATES_API_KEY : ${{ secrets.ALLRATES_API_KEY }} SLACK_WEBHOOK_URL : ${{ secrets.SLACK_WEBHOOK_URL }} A few caveats. GitHub's scheduled workflows are best effort: runs can start late, sometimes by much more than the interval, and can be skipped when GitHub is busy. Scheduled workflows are also disabled after 60 days without repository activity on public repositories. And on a private repository, every run uses Actions minutes. For an alert where timing matters, a dedicated scheduler is more reliable. Cloudflare Workers Cron Trigger version A Worker with a Cron Trigger runs on schedule without a server, and KV holds the state. wrangler.toml : name = "fx-alert" main = "src/index.js" compatibility_date = "2026-11-01" [triggers] crons = [ "*/15 * * * *" ] [[kv_namespaces]] binding = "STATE" id = "YOUR_KV_NAMESPACE_ID" Create the namespace with npx wrangler kv namespace create STATE and set secrets with npx wrangler secret put ALLRATES_API_KEY and npx wrangler secret put SLACK_WEBHOOK_URL . // src/index.js const RULES = [ { name : "USD/EUR" , target : "EUR" , above : 0.95 } , { name : "USD/JPY" , target : "JPY" , below : 140 } , { name : "EUR/GBP" , cross : [ "EUR" , "GBP" ] , above : 0.88 , below : 0.82 } , ] ; const valueFor = ( rule , rates ) => rule . cross ? rates [ rule . cross [ 1 ] ] / rates [ rule . cross [ 0 ] ] : rates [ rule . target ] ; const zoneOf = ( rule , v ) => rule . above !== undefined && v > rule . above ? "above" : rule . below !== undefined && v rule . below ? "below" : "inside" ; async function run ( env ) { const targets = [ . . . new Set ( RULES . flatMap ( ( r ) => r . cross ?? [ r . target ] ) ) ] . sort ( ) ; const res = await fetch ( `https://allratestoday.com/api/v1/rates?source=USD&target=${targets.join(",")}` , { headers : { Authorization : `Bearer ${env.ALLRATES_API_KEY}` } } ) ; if ( ! res . ok ) { console . log ( "rates request failed" , res . status ) ; return ; } const rows = await res . json ( ) ; // an array const rates = Object . fromEntries ( rows . map ( ( r ) => [ r . target , r . rate ] ) ) ; const state = JSON . parse ( ( await env . STATE . get ( "zones" ) ) ?? "{}" ) ; for ( const rule of RULES ) { const value = valueFor ( rule , rates ) ; const zone = zoneOf ( rule , value ) ; if ( zone !== "inside" && zone !== ( state [ rule . name ] ?? "inside" ) ) { await fetch ( env . SLACK_WEBHOOK_URL , { method : "POST" , headers : { "Content-Type" : "application/json" } , body : JSON . stringify ( { text : `${rule.name} is ${zone} ${rule[zone]}: now ${value.toFixed(4)} (mid-market)` } ) , } ) ; } state [ rule . name ] = zone ; } await env . STATE . put ( "zones" , JSON . stringify ( state ) ) ; } export default { async scheduled ( event , env , ctx ) { ctx . waitUntil ( run ( env ) ) ; } , } ; To send to Telegram instead, replace the Slack fetch with a POST to https://api.telegram.org/bot /sendMessage with chat_id and text , exactly as in the Python version. Test locally with npx wrangler dev --test-scheduled and then request /__scheduled . Cron Triggers run in UTC. A 15-minute schedule writes to KV 96 times a day, which is well within KV limits. Practical details Do not alert on errors as if they were prices. If the request fails, log it and skip the run. A 401 means a bad key; a 429 means you are over the monthly quota. Rejected 401, 403 and 429 calls are not charged. Say what the number is. Include "mid-market" in the message. A bank or card rate will differ; see mid-market vs retail rates . Use official rates for compliance triggers. If the alert drives an accounting or tax process, check the central bank's own rate with the central bank rates API instead. Faster checks rarely help. Rates refresh every 60 seconds, but a threshold alert every minute costs 15 times more than every 15 minutes. The article on how often exchange rates are updated covers what refresh frequency actually buys you. If you are still picking a data provider, the free currency API comparison lists update frequencies and quotas. FAQ How do I get an alert when an exchange rate crosses a threshold? Run a scheduled job that fetches the latest rate from an exchange rate API, compares it with your threshold, and sends a Slack, Telegram or email message when the rate crosses the line. Store the last state so it only alerts once per crossing. How do I stop the alert from repeating every run? Save the zone (above, below or inside) for each rule after every run, and alert only when the zone changes to above or below. When the rate returns inside the band, the rule re-arms. How many API requests does a rate alert use? One per run if you put every currency in one multi-target call. Every 15 minutes is 2,880 requests in 30 days, which fits the Small plan's 5,000 requests a month. Are GitHub Actions schedules reliable for rate alerts? They are best effort. Scheduled runs can start late or be skipped under load. For time-sensitive alerts, use a server cron or Cloudflare Workers Cron Triggers. Can I alert on a cross rate like EUR/GBP? Yes. Fetch both against USD in the same call and divide: EUR/GBP equals the USD/GBP rate divided by the USD/EUR rate. It costs no extra request. Originally published at allratestoday.com .

  • Hacker News Front PageThe Giantsread at source

    Pharo in the Browser with WebAssembly

  • Frontend Masters BlogMore Front-end Bloggers11 min read

    Creating Diagrams and Graphs Using Modern CSS

    The final part of the connectors series: dynamic connections that pick the closest sides, static connections that always leave from the same edge, and some bendy, zig-zag, and spring connectors, all built with anchor positioning, border-shape, shape(), and if().

  • Hacker News: Show HNLibraries, Frameworks, etc.read at source

    Show HN: BackTrail, keep each tab's Back history across browser restarts

    Comments

  • Hacker News Front PageThe Giantsread at source

    Atuin Threads: All your agent sessions on every machine

  • AbduzeedoMulti Author Blogsread at source

    Better Half’s Arcade Graphic Design for Brex’s Fiscal Mania

    Better Half / Cult Holdings builds a bullet-hell simulator and acrylic cabinet in its arcade graphic design for Brex’s Fiscal Mania. Destroying an unpaid invoice in Fiscal Mania would count as financi...

  • Hacker News Front PageThe Giantsread at source

    Apple is killing its Support Community forum next week

  • Hacker News Front PageThe Giantsread at source

    I asked every TV manufacturer 17 questions about how they spy on you

  • Hacker News: Show HNLibraries, Frameworks, etc.read at source

    Show HN: Whisperstream – Free, open-source local dictation for Windows

    Comments

  • Hacker News: Show HNLibraries, Frameworks, etc.read at source

    Show HN: AI Purgatory – how long until AI-exposed workers are back on track

    Comments

  • Hacker News Front PageThe Giantsread at source

    Show HN: Godbolt-in-a-floating-window for Neovim (C, C++, Rust)

  • Hacker News: Show HNLibraries, Frameworks, etc.read at source

    Show HN: Godbolt-in-a-floating-window for Neovim (C, C++, Rust)

    Comments

  • Hacker News: Show HNLibraries, Frameworks, etc.read at source

    Show HN: Zaelar, self-hostable life agent with voice, UI and network A2A

    Comments

  • Dan DaviesMore Front-end Bloggers9 min read

    Media Officer Role Breakdown

    The work behind the posts in Welsh football I do the vast majority of the things listed below as part of my role at Holywell Town. Social media, graphics, photography, video, interviews, website updates and the requests that come in around them. Individually, some might seem like small jobs. Put them together over a week […]

  • CSS WizardryTop Front-end Bloggersread at source

    Web-Perf Wednesday 017 – Prebid Stops Re-downloading Its Manifest

    Prebid 11.48 makes its jsDelivr manifest cacheable, removing 67 KB of repeat downloads for sites that use the web bundler.

  • SyntaxPodcastsAudioListen

    1055: What the hell is going on with GitHub?

    Scott and Wes sit down with GitHub’s Kyle Daigle to ask what the hell is going on with GitHub: the downtime, the agent traffic spike, and the infrastructure overhaul decoupling storage from compute. Plus Copilot Fusion’s cascade and critique modes, npm supply chain security, and…

  • Jeffrey Zeldman PresentsMore Front-end Bloggersread at source

    Better Day, Sunshine

    I haven’t driven a car in decades (New Yorker). But from my pre-New York life, I remember how sun glare could turn a pleasant road trip into a blinding gauntlet. Enter “Sunward,” a uniquely specific demo app my Automattic colleague Peter Westwood just whipped up in Spacefast. Spacefast – for your next idea Sunward – […] The post Better Day, Sunshine appeared first on Jeffrey Zeldman Presents .

  • Flavio CopesMore Front-end Bloggersread at source

    Letting an agent edit my videos

    How a coding agent edits my Borumi recordings through its MCP server: pause cuts, a word-by-word caption check, and the two checks I keep for myself.

  • Flavio CopesMore Front-end Bloggersread at source

    Why I replaced an MCP server with a CLI

    My Things MCP server made macOS ask for permission on every agent session. I replaced its 22 tools with a 598-line CLI and a skill, and here's what changed.

  • Net NinjaYouTube ChannelsVideoWatch on YouTube

    "Anti-Slop" Designs with Impeccable (a Crash Course) #1 - Getting Started

  • AbduzeedoMulti Author Blogsread at source

    Graphic Design: Adobe MAX 2026 Brings Play Back to the Canvas

    At Adobe MAX 2026 in Miami Beach, Adobe brought genuine joy back to graphic design with local Model Context Protocol integrations and playful tools. Design is supposed to feel good, but for the past t...

  • Lea Verou’s blogTop Front-end Bloggers5 min read

    Candidate Statement for the 2026 W3C TAG Election

    Group photo of the W3C TAG in July 2022 with me to the left of Sir Tim Berners-Lee (inventor of the Web and TAG chair emeritus). Photo credit: Dan Appelquist I had the great honor of being elected to the TAG by the W3C membership in 2020 and again in 2022 , serving two terms…

  • Twilio BlogMulti Author Blogsread at source

    PHP2Shell. What is it and What Do You Need to Do?

    You don't need to be a PHP developer or a systems administrator to have heard about the PHP2Shell exploit. In this post, learn what it is and what you need to do going forward.

  • Twilio BlogMulti Author Blogsread at source

    Transitioning Your Communications: How to Keep Customer Messages Flowing Seamlessly

    Learn how to transition your messaging workflows from Tyntec to Twilio without interruption while maintaining data privacy, WhatsApp reputation, and more.

Tuesday, 10 November 2026

  • AbduzeedoMulti Author Blogsread at source

    Soft Labor: Tactile SVG Web Design by Other Means

    Other Means pairs fuzzy SVG typography with hover-triggered focus in its editorial web design for Soft Labor, featured on Hover States. Landing on Soft Labor, the cultural research publication written...

  • Mux BlogCompany/Startup Blogs5 min read

    How a designer learned to unsee Will Smith eating spaghetti

    How Mux’s Creative Director designed and built a custom Video.js v10 skin himself, using Figma to design, Claude Code to build, and the browser to review.

  • SitePointThe Giantsread at source

    How to Improve Email Deliverability Rates

    null Continue reading How to Improve Email Deliverability Rates on SitePoint .

  • AbduzeedoMulti Author Blogsread at source

    GoFundMe Brand Identity Is a Masterclass in Branding by Koto

    Koto evolves the GoFundMe brand identity around one idea, Help Adds Up, turning the goal bar into a progress circle that radiates outward. The progress circle is the whole idea. GoFundMe's most recogn...

  • AbduzeedoMulti Author Blogsread at source

    delaCrem: Artisanal Gelato Brand Identity by Mucho

    Barcelona studio Mucho unifies Massimo Pignata’s three delaCrem gelaterias under one brand identity with Pol Montserrat’s illustrations. Gelato maker Massimo Pignata helped spark a slower ice cream cu...

  • Flavio CopesMore Front-end Bloggersread at source

    A deep dive into Businesses in ChatGPT

    Businesses in ChatGPT lets you claim your business, add a product feed, see mentions and clicks, and publish a Business Agent. How it works and its limits.

  • Adrian RoselliMore Front-end Bloggers4 min read

    My Interop 2027 Picks

    Interop 2027 is gathering votes and the deadline is 19 November 2026. For the second year now, Firefox made a page to let you rank your Interop 2027 choices. Which I’ve now done. I narrowed it down to fifteen, though getting there was oddly difficult. It seems the top three…

  • AbduzeedoMulti Author Blogsread at source

    Brand Identity: Connected Pets Sub-Brands by HOW+HOW

    Branding agency HOW+HOW partners with the UK’s largest pet retailer to evolve Pets into an energetic ecosystem of distinct, connected sub-brands. Brand Identity: Taming a Retail Behemoth Anyone who sh...

  • Jim Nielsen’s BlogMore Front-end Bloggersread at source

    Making a Website in 2026

    Pete Millspaugh has a great post over on the Val Town blog where he documents his attempt at making generating the same website across a variety of AI tools. I love the post. Here are a couple of thoughts that come to mind. The Space Is Ripe for Referral Fees Pete decided to make a website for his conference talk, and he added a comments section to make it a less static / more dynamic demo. This is interesting because LLMs are really good at generating one-off HTML pages you open at file:// , but as soon as you start to introduce technological dependencies — like a database, or an analytics tool, or a service for sending email, or payment processing, etc. — the question becomes: How does the LLM choose which service to rely on? It’s one thing to have the LLM choose React over Angular or Vue over Svelte. But how (and why) does it choose something like an auth provider? Does it build you a site with Auth0 or WorkOS or Clerk? How would a non-technical user even know how to weigh the trade-offs of making such a decision? Pete said he was deliberately vague about these details, which is why I find his experience so intriguing. For example, here’s what he found after generating a site in Lovable: My Lovable app is fullstack React, I learn, clicking around to find the code. It seems a bit heavy for a single-page site what with dozens of files […] The comments database seems to be using Supabase, which required no intervention on my part to set up. There’s a whole lot more—analytics, e-commerce and AI integrations, etc. I’d be really curious to know what that “a whole lot more” was. Which tools/services did it choose to depend on? How many people creating these sites understand that, even for a website that’s mostly static content, they’re being given an output that ships a JavaScript application framework, router, hydration runtime, analytics, component bundles, etc.? You asked for a sailboat, but you got an aircraft carrier . Do you know? Or more importantly, do you care ? But it’s more than just a choice of languages and frameworks. It’s also a choice of third-party services. Why Supabase? Why not Firebase? Or Neon? Speaking of Neon, here’s Pete’s experience with Vercel’s v0: I copy-paste my prompt into the chatbox. After 11 seconds, v0 asks if I’d like to install Neon, because my comment wall needs a real database. Sure, why not. (Does that mean I need a separate Neon account? Vercel creates one for me, and later I’ll find a welcome email from Neon in my inbox.) Frankly, this all feels like a space absolutely ripe for those kinds of business referral transactions you find in other industries. Take real estate, for example. For lots of folks, when you buy a home you need an agent. (Not that kind of agent; a real estate agent.) Your real estate agent is there to help you answer all those common questions that come along with buying a home: You: “How do I finance this?” Agent: “I know a mortgage company…” You: “How do I know the house is in good shape?” Agent: “I know an inspection company…” You: “We found X during the inspection, who can address it?” Agent: “I know a company that does {roofing | electrical | plumbing | etc.}…” You: “How do I close on this deal?” Agent: “I know a title company…” While you’re free to make all these decisions yourself, your agent is also there to help you pick. Presumably they’ll choose a company that reflects well on them, but there’s also undoubtedly good money to be made in referrals. After all, who else is dealing with people with these kinds of needs all day long? The “make a website with AI” space has a parallel here which makes you wonder about the incentives behind the LLM’s decision-making. Why Supabase over Firebase? Because the LLM reasoned about it? Because, statistically speaking, it was the next most probable word? Because Supabase paid Lovable (and hopefully disclosed such preferential integrations)? If LLMs are already unpredictable and non-deterministic, nothing will make their actions more predictable and deterministic than some financial incentives. Who is the customer here? The person generating the website? Or the company willing to pay for its services to be embedded in generated code? (That’s a lucrative business waiting to happen…) Get Ya Some Imperial Credits As a customer, the credit systems behind these LLM systems are annoying as hell. Here’s Pete: I’m using up “credits” as the machine works—0.70 so far, now 2.30, now 3.10, oh boy—and I have no clue what that means. There are no units. I’d prefer dollars. Are credits just dollars? As it turns out, no. With another tool: I do two more rounds of click “Continue generation” and wait until my website is ready: 5 minutes 36 seconds for 0.070 credits, and 1 minute 29 seconds for 0.024 credits. It’s probably just me, but c’mon, who do these “credit” systems really benefit? When my prompt is running, don’t show me usage like this: Credits: 0.05 -> 0.0672 -> 0.0871 Just show me the cost: Cost: $0.02 -> $0.21 -> $0.31 Don’t make me do the conversion of credits -> dollars on the fly. Computers are good at math. [soap-box] I know it might hurt your bottom line if I’m not thinking about how much money credits I’m spending, but c’mon, can we stop with the financial abstraction on top of financial abstraction? I hate how so many hosting companies are doing this “credits” for compute primitives thing. It’s pitched as if spending credits is easier, because you can apply them across services, e.g. buy image transformations or serverless function compute, all with credits! Yeah well guess what other financial abstraction I can apply to all those different services? Dollars and cents. [/soap-box] Conclusion Pete’s conclusion: I am both optimistic and pessimistic about this moment for making websites. I’m optimistic because end-user programming, the idea that everyone should be able to customize their own software, seems more possible than ever. I’m pessimistic because a slopsite, an AI-generated website with little to no human thinking involved, is often worse than no website at all. That resonates — and feels generalizable. You fill in the blank: An AI-generated ___ with little to no human thinking involved, is often worse than no ___ at all. My conclusion? Making Generating a website may be easier than ever. Understanding what you’ve generated, who you now depend on, and why? Not so much. Reply via: Email · Mastodon · Bluesky

  • DigitalOcean BlogMulti Author Blogs2 min read

    QM, Y Combinator's Open-Source Agent Harness, Now Deploys in One Click on DigitalOcean

    Y Combinator built QM, a multiplayer agent harness for Slack and the web, to run agents across its own teams, from accounting and legal to engineering. In July, YC open-sourced it so any startup could do the same. Starting today, DigitalOcean offers the first one-click…

  • Adam ArgyleTop Front-end Bloggersread at source

    WWW Ep259 Your AI Agents Dont Need You Anymore

    Ep #259 Your AI Agents Don't Need You Anymore Robbie , bourbon and whatnot. We chat Doorway , dark factories, gauntlet loops that beat the slop out of your code, the Creator Micro , Glimmer getting jelly of Claude, engineering vs. coding, CSS scroll axis , and Jeff the mower…

  • Engineering at MetaCompany/Startup Blogs8 min read

    Your Algorithm: Giving People More Ways to Personalize Facebook Recommendations

    Your algorithm is a suite of AI-powered tools that makes Facebook recommendations more transparent and steerable, with a single editable view of the interests that shape Feed and Reels. We’re sharing how Your algorithm turns people’s natural language preferences into responsive recommendations, and how LLMs are tailored to specific tasks within the system. Traditionally, the [...] Read More... The post Your Algorithm: Giving People More Ways to Personalize Facebook Recommendations appeared first on Engineering at Meta .

  • Engineering at MetaCompany/Startup Blogs17 min read

    How We Built TLX: A Hardware-Native GPU Compiler for Production ML at Scale

    Triton Low-Level Extensions (TLX), is an extension to Triton that helps engineers optimize GPU performance while preserving Triton’s productive programming model. TLX introduces explicit warp-group and cluster-level orchestration, letting developers adopt new hardware capabilities without sacrificing velocity. TLX has demonstrated performance improvements in RecSys training, RecSys inference, and large-scale LLM pretraining. Modern GPU architectures increasingly [...] Read More... The post How We Built TLX: A Hardware-Native GPU Compiler for Production ML at Scale appeared first on Engineering at Meta .

  • CSS-TricksThe Giantsread at source

    CSS Optimization: Is Using Declarations Just Once (UDJO) Still Effective Today?

    Is the technique inspired by the DRY principle by identifying and reducing (if not eliminating) CSS declaration repetition still a relevant optimization strategy? CSS Optimization: Is Using Declarations Just Once (UDJO) Still Effective Today? originally handwritten and published with love on CSS-Tricks . You should really get the newsletter as well.

  • CodePenCompany/Startup Blogs1 min read

    448: CSS Modules is Cool Again

    Shaw and Chris talk CSS Modules, a rather old bit of technology in the CSS-in-JS category. CodePen now supports them, which took a little work because the way they process code is a smidge unusual. We use CodePen Modules (with Sass!) here at CodePen so we knew we wanted to support them, but we got […]

  • AbduzeedoMulti Author Blogsread at source

    Inside the Craft of Zap Energy Brand Identity by Manual

    Manual builds the Zap Energy brand identity around a diagonal wordmark, electric yellow and vivid orange, for the integrated nuclear company. The wordmark is the whole event. Three thick geometric let...

  • W3C BlogBrowsers, engines, etc.5 min read

    The multiplier effect: How authoring tools can transform web accessibility

    In this post, W3C's Web Accessibility Initiative (WAI) Director Shawn Lawton Henry covers how authoring tools can help make vast amounts of content more accessible with less effort.

  • Adam ArgyleTop Front-end Bloggers6 min read

    PropFLIP

    FLIP, but the invert is 4 custom properties and CSS does the play

  • SitePointThe Giantsread at source

    How to Use Developer Time Tracking Data to Improve Sprint Planning and Estimates

    null Continue reading How to Use Developer Time Tracking Data to Improve Sprint Planning and Estimates on SitePoint .

  • Christian HeilmannTop Front-end Bloggers3 min read

    Pardon my dust 2 – redesigning christianheilmann.com

    OK, the shift from WordPress and Godaddy is happening. I managed to convert most of the old blog to static pages and you can see the result already at the old domain Wait-till-i.com It’s a highly responsive and fully static site now, build with lots of dirty PHP conversion scripts and Jekyll. I did use […]

  • AdactioTop Front-end Bloggersread at source

    Six Gutenberg Revolutions in a Row—Asterisk

    An interview with Ken Liu and Ada Palmer: The vast majority of computing power on Earth is human brains, and a huge amount of it is dedicated to worrying about where lunch is coming from tomorrow. If you want to free up computing power, solve poverty. adactio.com/links/22833

  • AbduzeedoMulti Author Blogsread at source

    Book Design: The Hudson Transparencies by Pentagram

    Pentagram’s book design for Pentagram Papers 52: The Hudson Transparencies calibrates ink density and showthrough for 58 Victorian plates. When Annie Hudson pointed her sons Luke Powell and Jody Hudso...

  • Netlify BlogLibraries, Frameworks, etc.read at source

    Netlify goes full stack: introducing Netlify Server

    A long-running Node.js server for your backend: WebSockets, real-time apps, AI agents and in-memory state, deployed like the rest of your Netlify project.

  • The Mozilla BlogBrowsers, engines, etc.2 min read

    Firefox’s free built-in VPN uncaps data limit through January 2027

    Whether you’re heading home to see family this winter or taking a well-earned break before the year wraps up, we want you to browse with a little more peace of mind. That’s why, as our gift to you, everyone using Firefox’s free built-in VPN gets unlimited data starting today, November 10, through January 31, 2027. […] The post Firefox’s free built-in VPN uncaps data limit through January 2027 appeared first on The Mozilla Blog .

  • HeyDesignerDeveloper/Designer Newsread at source

    The beige era of product design

    On borrowing Apple's type scale, The new full-stack designer (video), AI prototyping inside your design system, Introducing Adaptive Dark Mode and more.

  • Frontend Masters BlogMore Front-end Bloggers3 min read

    My Interop 2027 Picks

    What web platform features are we all wishing for perfect cross-browser interoperability next year?

  • freeCodeCamp.orgYouTube ChannelsVideoWatch on YouTube

    Codex & AI Coding Agents - Learn Agentic Coding

  • SitePointThe Giantsread at source

    What Breaks When a Self-Hosted LLM Gateway Hits 1,000 Concurrent Streams

    null Continue reading What Breaks When a Self-Hosted LLM Gateway Hits 1,000 Concurrent Streams on SitePoint .

  • Flavio CopesMore Front-end Bloggersread at source

    API pricing vs subscription: what my month of Codex would cost on the API

    I priced 30 days of my real Codex usage at API rates. A $100 plan covered $745 of API-equivalent work. Here's the math, the script to run on your own logs, and when the API still wins.

  • HeyDesignerDeveloper/Designer Newsread at source

    AI skills for building better interfaces

    Iconden: The iPhone app icon gallery, "x is dead" is dead, When touch makes sense on a laptop

  • HTML All The ThingsPodcastsAudioListen

    Choosing the Right AI Model for the Job

    Are you wasting money and usage limits by choosing the most powerful AI model for every task? Or are you wasting even more time fighting with a cheaper model that isn't up to the job? In this episode, Matt and Mike discuss when to use smaller, faster AI models and when it's…

See everything from the last 30 days →

Articles belong to their publishers. This site only collects what their feeds provide.

Updated 11 November 2026 at 15:00