Feed the-daily-wtf The Daily WTF

Favorite IconThe Daily WTF

Link http://thedailywtf.com/
Feed http://syndication.thedailywtf.com/TheDailyWtf
Updated 2024-04-28 05:17
Error'd: (Almost) Nought but Nulls
For this week, it's mostly nulls. But first, Finn Samiprovides a provocative hint that the LLMs might bedeveloping a theory of other minds. Says Sami: "A coworker noted that when Copilot starts to have doubtsabout Bing, this might be the real start of AGI." I've certainlygot my doubts about some of the Alleged GIs around.
CodeSOD: A Nice Break
Once upon a time, HTML had tags like <marquee>, which scrolled text across your page, and when combined with animated GIF backgrounds basically defined the Geocities aesthetic.Since then, the HTML specification has been refined, and the choice has been made that HTML tags should (mostly) be about semantics- describing the structure of a page, the meaning of elements, and the relationship between those elements. It generally shouldn't describe the presentation of those elements (CSS should do that)- even though the semantics generally imply something about the display (paragraphs and divisions are block elements, for example).And then <br> walks up and throws a wrench in that, because it's much more about the layout of a page than the semantic relationship between elements.The <hr> tag falls into a similar space- it does represent a logical break between parts of the page, but also has a clear visual intent: draw a line on the page.I'm not trying to suggest that either of those tags are wrong or shouldn't be part of the spec, I'm just highlighting that they're not precisely semantic- they don't have a pure semantic meaning like a paragraph or a logical division might.But you know what is wrong? This HTML Ted found, which is a different way of getting a horizontal rule on the page.
CodeSOD: Evaluating Perks
Today's anonymous submitter works for a company that handles customer rewards perks. It's handling thousands of dollars of transactions a day, which isn't a huge amount, but it's certainly non-trivial.Now, there's a conversion formula from points to dollars: points/100*1.7. Now how would someone implement this complex formula in PHP? Well, our submitter's predecessor did it this way:
CodeSOD: TrUe Romance
Mario's predecessor was writing some Java code that parsed textual input. Thus, it needed to be able to turn the string "true" into the boolean value true, but also needed to handle "True" and "TRUE".Now, in Java, the obvious answer would be to use the equalsIgnoresCase String member function. if (value.equalsIgnoresCase("true")).... I mean, obvious to you or me, anyway.But if you're a developer with a hammer, you're going to turn every problem into a nail. And when that hammer is regular expressions, you're going to turn every problem into two problems.
CodeSOD: Capitalizing on Memories
Gavin inherited some "very old" C code, originally developed for old Windows systems. The code is loaded with a number of reinvented wheels.For example, they needed to do a case insensitive string comparison. Now, instead of using stricmp, the case insensitive string comparison, they wrote this:
Error'd: A Brand New Bag!
We've all been watching programming gaffes come and go, and they're almost alwayssome variation on a set of usual themes I've been trying to classify and name. But today, we've got one for you that is amazingly, excitingly, fabulously novel! I'm calling this one ecrash unless somebody's got a better idea.Not to keep you waiting any longer than absolutely necessary, here's Ian M.with this week's winner:"I don't even know how you do search like this, it'ssomehow both way more complicated and way moreunsettling then it needs to be."
CodeSOD: Input Validation is a Sure Thing
Validating inputs matters. It's also a challenge. Validating that an input is numeric might be easy, but validating an email address is orders of magnitude harder (and technically isn't a regular language and thus can't be parsed by regex, though you can get close). Validating a URL is also a pretty challenging task, since URLs can contain all sorts of surprising information.Daniel's co-worker, when tasked with validating URLs, looked at the complexity, and came up with a simple, elegant solution, in JavaScript.
CodeSOD: A Caught Return
When John takes on a new codebase, he always looks for low-risk ways to learn the code by changing it. Things like beefing up the unit tests, tracking down warnings that have been left to languish, minor quality-of-life changes.Well, a few years back, John inherited some C# code, and started tracking down some warnings. That lead to this method.
CodeSOD: Free From Space
Henrik H is contracting with a client, and that client uses a number of other contractors. Some of them have... interesting approaches to problem solving.For example, one of the servers is a Windows server, which stores a lot of temp files on the D: drive. So someone needed to write a C# function that would check the available space, and if it exceeded some threshold, delete the temp files.This is what they came up with.
CodeSOD: Zero Failures
Parsing strings into other data types is always potentially fraught, what with the edge cases and possible errors. This is why most languages provide some kind of helper methods that try and solve those hard problems.C# has a number of them. One, for example, would be Int32.Parse- it attempts to parse a string into an integer, and throws an exception when it fails. Similarly, there's an Int32.TryParse function, which avoids throwing an exception and returns an error code instead.Which brings us to this code, from A Barker.
Error'd: Here We Go Again
This week has been mostly centered on a holiday forthe USians. In the era of online retail and theglobal dominance of Amazon, the notion of "Black Friday"seems to have spread further than the harvest festivalitself. The practice of mass national migrationsthankfully has not.Migrating Maia exclaims"Oh wow, I can change my flight for a fee of only [AMOUNT]! What a deal!"
Best of…: Classic WTF: Worse Than Failure
CodeSOD: Lines of Code
Brittany is a game developer, and frequently ends up working on contracts for other companies. One company wanted her to add some features to their trading-card based game, and they offered her an option: she could either do a fixed-rate contract or a paid-per-line contract.Brittany went with the fixed-rate.When reviewing the code, she found that the previous developers went with the per-line contract:
CodeSOD: Limited Space
While XML is a complicated specification, and incredibly bureaucratic and verbose, it's also powerful enough that many languages, from Java to Python, have XML helper classes in their standard library.C# is one of those languages. But just because there's a built-in library (and a wealth of 3rd party libraries with richer features) doesn't stop people from reinventing the wheel.Ryan sends us this short C# snippet, writing: "This is the start of a method that constructs an XML file using strings."
CodeSOD: Constant Adventure
We know that June 7th, 2006 was a long day for Jonas, Rusty's long-ago predecessor. We know that, because Jonas made a big commit that day. It was the day someone told him to stop using magic numbers and switch to named constants.
Error'd: Unrewarding Math
Our own Michael R., still job-hunting, has turned up a position that should NEVER be filled. "I was hoping at least one of them goes to 11!" Rock on.
CodeSOD: Mapro
Steinin was doing some work for a company that needed some geographic information systems work done. Steinin was just the programmer, and was no expert on the algorithms they needed implemented, and so asked for some references on how to implement them.They handed him this C code.
CodeSOD: Shift Sign Off
An anonymous submitter was working with some vendor-supplied code for talking to a network device. They sent along this sample C code for properly populating the required header.
CodeSOD: All Roads
A conditional statement represents a branch in our code. A place where things could go one way, or they could go the other. That, at least, is traditionally what they are. Adam's co-worker took a different approach.
CodeSOD: Include This
C and C++ are weird, specifically in the way they handle the difference between declarations and definitions. We frequently put our declarations in a header (.h) file, and the definitions associated with them in an implementation file- say, .cpp.But things can only get declared once, which means if multiple implementation files depend on the same header, the compiler will yell at us. We need a way to ensure that, no matter how many times a header is referenced, it's only actually read once.For some compilers, you can use #pragma once to tell the compiler to only include this file once. But that's compiler specific, so more traditionally, the formula is something more like this:
Error'd: Cheap Date
Poor Michael R. is STILL job hunting. "Suddenly it all makes sense why I can't find anything: there are -1 more positions available."Good luck Michael, we're counting (badly) on you.
CodeSOD: On the Border of Badness
The WebForms APIs in ASP .Net tried to make web programming look like native programming- you designed pages in a WYSIWYG editor, and bound event handlers to controls, and pretended like the request/response cycle didn't exist. It was a bit of a mess.One of the ways it was messy was that you now had two different approaches to styling elements in your UI. You could go the route of using CSS, like a normal web page. But every web Control object also exposed a bunch of properties that you could access directly from your server-side code, allowing you to do things like myControl.BorderColor = Color.Red.All that's fine and good, or fine and bad, if we're being honest, but it brings us to Leandro's submission. While trawling through a legacy code base, this little snippet leap out:
CodeSOD: UniQQue Naming
The application Zach B found himself supporting needed to accept file uploads. At one point, someone decided to just drop all the files in the same directory, so they needed to find a way to ensure that there were no name collisions.They wrote this:
The White Appliphant
Circa 2010, Becca's employer, Initech, was growing. "Growing", in this case, meant "acquiring competitors who had niche products that their customers wanted added to Initech's portfolio".One such product was a content-management/workflow tool, already sold to some big-name, multi-billion dollar companies. The tool fell into that niche between "really useful to the people who need it" and "buggy as an ant hill inside of a termite mound under a wasp nest". Sales were good, but the devs were underwater and the backlog of feature requests and bug fixes were growing. So Initech bought the vendor, fired most of the developers, and handed it to an Initech team."It's just some bugfixes," management said, "what could go wrong?"The first thing that went wrong was the senior dev assigned to the project rage quit after two weeks with it. "I ran the install scripts, which are supposed to provision a new database and deploy the web app files to a web server. There are thousands of interlocking scripts to make this happen, and half of them don't work. Along the way, it creates hundreds of SQL tables, all with names like table1, or important_table1, and many of them are never queried by anything in the code, despite having data." What followed was an ultimatum: take me out of this project or I'll take myself out of the company.Given that this senior dev was central to many other projects, the company shuffled things around and put another sacrificial lamb in place, a different senior dev. This dev needed to add a small feature- a new field to a screen. Six weeks later, the dev had something that mostly worked. It was a hacked on retrofit- there was no "right way" to add a new field to a screen; any modifications to any of the front end or back end or database code tended to cause explosions, thunderstorms, and the gnashing of teeth. This dev, also very high up and respected, repeated the ultimatum.When adding a new field to a screen takes six weeks and burns a developer out, you can imagine how hard it is to fix bugs. The junior devs put on bug fixes were burning out just as quickly, but producing far fewer results. The bug backlog kept growing far faster than bugs could get patched.
Cool Power
Power outages are never good, and they're even worse when your facility needs to run 24/7. Now, Jaroslaw's organization didn't do a great job setting up for round-the-clock, always-on operations. It was the kind of thing where the organization grew, annexed the neighboring building, and kept growing. The result was hundreds of workstations, two separate power lines, two server rooms, three different Internet uplinks, and huge piles of switches responsible for making this network work.Which added the problem that after a power outage, nothing came back on exactly right, either. It always took some time to find the one switch that opted not to reboot.Now, many years earlier, someone had the bright idea of installing a generator. The hookup offered no easy way to switch over to generator power, and thus required an electrician with keys to the elecrtical boxes to actually make the change. While the servers had small UPSes, enough of the environment went down during a power outage that, by the time they had the generator on and everything powered back up properly, the outage was usually over.And so it went for years, until someone higher up looked at the problem and freed up the budget to fix things. The generator was replaced, and there was a plan to change the wiring so that it was faster to switch over- but it turned out that would have tripled the budget and shut the facility down for days while electricians redid the whole electrical system. Instead, the budget was used to upgrade from small, consumer-grade UPSes to a big hocking, 10kW unit.It was the size of a large refrigerator, and had enough power to keep all the critical elements of the facility powered on for twenty hours- time enough to switch over to the generator, if needed.And then, miracle of miracles, they tested their switchover plan. They cut main power, saw the UPS come on, ensured work could continue, then had the electricians switch on the generator, and then reversed the whole process. It went off without a hitch.And then a week later, the UPS screamed about an overload. It lasted for about 40 seconds, then cleared up. Considering that the UPS had way more capacity than they needed, that seemed like a serious problem. Two hours later, it happened again. And again. And again. Jaroslaw went through everything in the server room, trying to find the badly behaved device. At one point, he found an unplugged electric kettle sitting not far from the server room, and went on a hunt to see if anyone had been making tea in the server room, thinking that was the culprit. No one had.Over three days, after checking all the equipment, Jaroslaw went to the building wiring diagram and started checking every outlet. He found one, hidden in the back of the server room, ostensibly unused, that had an extension cord plugged in. The cord was neatly tucked into the cable chase, as if it was part of the plan. Jaroslaw tracked the cable, and followed it around the room until he found a hidden refrigerator. Some of the 24/7 staff wanted easy access to snacks and drinks, and didn't want to constantly badge in and out of the server room to get them. While there were plenty of non-UPS protected outlets they could have used, someone had decided this was a better option.And sure enough, while Jaroslaw was looking at the fridge, he heard the compressor kick on, and the UPS scream about an overload at the same time.The immediate fix was easy: remove the fridge and extension cord, and have a serious discussion about proper server room safety. The longer term fix was spending the last bits of the budget to add keyed switches to all of the outlets in the server room, ensuring no one could plug things in without going through the proper channels. [Advertisement] Keep the plebs out of prod. Restrict NuGet feed privileges with ProGet. Learn more.
Error'd: A tall glass of {{product.brand}}
Hungry Adam starts off your RSS feed with what might honestly be making the best of a bad situation."I guess my pizza will be here next time I write a date bug?"
CodeSOD: Not My Domain
Dian found this code snippet.
CodeSOD: UTF-16 Encoding
Let's say you were browsing through some code, and saw a function signature like this:
CodeSOD: Legacy Horrors
Today is Halloween, a day filled with chills, horrors, and Jamie Lee Curtis. An interesting aspect of horror movies is how often the roots of the horror lurk in the past. Michael Meyers had been in an asylum for decades before his infamous Halloween rampage. Midsommar represents a centuries old tradition. Barbarian is rooted in sins committed a generation prior. Freddy Krueger was the manifestation of the sins of our protagonists' parents. Hell, even Dracula is a menace that had been lurking for centuries before our story begins.In honor of that, we're going to look at some code from Davide. Like so much classic horror, the seeds of this sin were planted many, many years before Davide arrived.In 1991, Microsoft released their first version of Visual Basic. The language evolved until 1998, with the release of VB6. Mainstream support ended in 2005, extended support ended in 2008, but like true horror, VB6 has not truly died. The development tools continue to run on all 32-bit versions of Windows.Back in those olden days, Davide's predecessors decided to implement an Enterprise Resource Planning system in VB6. It grew, and evolved, and became something that couldn't be controlled anymore- it was 2.5M lines of code. It entangled itself into the company, taking over every core business function, and rapidly becoming indispensible.We can compare it to so many monsters of horror- the shapeshifting Thing, gradually replacing parts of the company with itself. The fungus from The Last of Us, taking over the brain of the company. We can compare it to The Blob, which may also be the most accurate description of the coding practices used in building it.Here's some code that evaluates a formula entered by the users:
CodeSOD: High Performance Query
Aaron was doing some work with a high-performance-computing platform. The kind of thing that handles complex numerical simulations divided across farms of CPUs, GPUs, FPGAs, and basically anything else that computes.The system comes with a web GUI, and the code for the web GUI is... well... let's just look at the comment on a method.
Error'd: Look, Up In the Sky!
"My FB got HACKED" wrote an anonymous contributor. "Verification codes box sooo not funny " Not just one facepalm, but four.
CodeSOD: End this Date Now
Once upon a time, someone wanted to store date ranges in a database. And so, they started down the obvious path of having two columns, one to hold the starting date of the range, and one to hold the ending date. But even going down that route, they tripped and fell, because they ended up with two database fields name Startdate and StartdateStart. Startdate was the end of the period.You might be thinking, "Well, at least they used the database datetime types," but on that front, you'd be wrong. They used text, and in some cases, instead of Startdate holding a date, it held a string in the format 22-08-2022 to 27-08-2023.Someone had to write code to parse that data, and that someone did the job and then left. Which meant Niels H had to support the resulting C#.
CodeSOD: It's All Right
Mark recently inherited a Java codebase that... well, it's going to need some support. He thought things were bad when he encountered this:
Locally Variable
Henrik H was hired by a customer to fix some bugs in their application. The main one was that their C# web app didn't properly track the user's culture settings. Henrik spoke with their internal developer, who originally wrote the application, and was told: "Sometimes the culture name and LCID is out of sync. I don't understand why?"Well, from that description, Henrik didn't understand either. Why was there a separate name and what the heck was LCID? Why were two variables getting synced? The only answer would be in the code, so Henrik dug in.
CodeSOD: Cast a Different Spell
The rule of spelling in programming is that it doesn't matter if you spell it correctly, only if you spell it consistently. Which is fine if you're working alone, but we rarely work alone. And unless you're entire team shares the same misspelling habits, you're in for trouble when one person insists on "calander" while everyone else uses "calender".Fortunately, Jonathon's co-worker had a solution to this problem.
Error'd: Self Driving Level NaN
This week, Harry Altman teaches us how to make sure thatautoautomobiles don't crash into your house. It's notimmediately obvious, and the answer to the puzzleborders on philosophical.But first, an anonymous reader wrote"I found this in a practice exam for an Azure Certification, !(Azure Service Bus) story."I believe him.
CodeSOD: Set Your Performance Target
The power of SQL is that you describe to the database what you want, and the database figures out how to execute that query as efficiently as possible. This means that, at least in theory, you optimize your database access not by changing the query, but by tuning the database to run that query efficiently.In practice, every database has quirks, and frequently you do tune the query a bit, to trick the optimizer into running it efficiently. And sometimes, you have to modify the query because people are dumb.Jakard was tracking down a performance issue in the database. There was one query that was taking over 30 minutes to produce less than 30 records. That seemed bad, so Jakard took a look at the query.
CodeSOD: Write, Write Again
Melissa was trying to figure out why an old application wasn't writing out a data file when commanded to do so. It was an implemented feature, it should work, it had worked at one point- but it wasn't working now.She traced through the C code and found this:
CodeSOD: Taking the Temperature
Mr. TA inherited some C# code that communicates with a humidity and a temperature sensor. Each sensor logs a series of datapoints as they run, and can provide them as an array of data points.This leads to this code:
CodeSOD: If You're First
Laurie has been supporting an internal application for a few years. The code is a mess, and while she wasn't at the company for the early stages of development, tales are told about it- it was chaotic, it was badly estimated, it was wildly over budget, the latter phases were a crunch where ten developers were shoved onto the project at the last second.Today, it works- mostly. But it provides plenty of support tickets for Laurie, and demonstrates some WTFs.One feature is that many forms have a "RESET" button. Push the button, clear out all the values on the form. Here's how that code looks:
Error'd: Cold Shoulder'd
Winter is coming to New Mexico this year with a vengeance. It should be fine if you just stay out of the wind.First, to get this out of the way,never let it be said that we at TDWTF won't take our own lumps.The Beast in Black has a beef with our grammar, and technically he is correct."So Meta, Especially This Atrocity..."
CodeSOD: Nada
In the cinematic classic They Live, the protagonist is a drifter named "Nada"- nothing. Zizek derives much meaning from this, which is an entertaining rant if nothing else.But Nada appears in other places, like this code found by JMM.
CodeSOD: Testing with a Lisp
Dom works on a codebase which has fallen victim to Greenspun's Tenth Rule. Yes, they've implemented a user customization system that is an "ad hoc, informally-specified, bug-ridden, slow implementation of half of Common Lisp."Said Lisp implementation started its life as a Java backend, but over the years got ported into Flash apps, iOS, and most recently the JavaScript front end.While reviewing some of the tests on the JavaScript parser, Dom found some questionable understanding of JavaScript.
CodeSOD: Table This Until Tomorrow
One of the secret powers of a relational database system, is that the query describing the data you want and the execution path for fetching it have no direct connection. The database is free to find optimizations and alternate paths for fetching the data. Coupled with database configurations like indexes and partitions, you can frequently take huge quantities of data and run arbitrary queries against it without having to think too hard about performance when writing the query (while spending a lot of time thinking about performance as you manage indexes and statistics gathering).That doesn't mean that we don't see some... unique choices in terms of how we organize our data "for performance". Chris needed to add a field to one of their data models. Said data model was generated from object oriented mapping, so it seemed like it should be easy to do. Just add a field to the object oriented model, generate a migration script, and then start the rollout process.It was not, in fact, easy. At first it was because whatever they had done in their underlying configuration meant that the ORM tool they were using couldn't successfully generate migration scripts. Fine, Chris could generate it by hand- it was one field after all. But applying that change in a test database showed that just adding the field wasn't sufficient- a bunch of materialized views needed to be modified to also fetch the field. Upon opening the code for the views, Chris also saw that simply changing the views wasn't going to be sufficient, because they didn't work the way anyone expected.Each view queried many hundreds of tables, all with the same schema. They were all named in the pattern MyDataset10OCT2023. Each one had a foreign key back to the previous day's table- MyDataset10OCT2023 linked back to MyDataset09OCT2023, which went back to MyDataset08OCT2023, and so on. The views joined all of those tables together to aggregate the data across the entire history, essentially summarizing a daily snapshot of all the ways the data changed.All of this was managed by a set of PL/SQL stored procedures, some of which generated the daily table, some of which regenerated the queries for the materialized views. All of this meant that Chris either had to add the field to every single daily table, or had to touch PL/SQL code that munged strings together to generate SQL queries that replaced materialized views.Given the options, changing every single table seemed easier and at least something that could be automated and tested. But Chris has no idea why there's essentially a linked list of database tables with daily snapshots, and asking the people who had been on the team for awhile didn't yield better answers than, "I think they did that for performance." [Advertisement] Utilize BuildMaster to release your software with confidence, at the pace your business demands. Download today!
CodeSOD: Type of Expression
Several months ago, Rebecca's predecessor wrote some Perl code to traverse a JSON-esque data-structure. It was an hash map that could contain values, arrays, or more hashes.Said predecessor wrote the code, had it reviewed, and tested it. It rolled out in production, and for three months, everything was fine. Three months was also long enough for predecessor to take a new job elsewhere, which left Rebecca as the sole support when it broke.
Error'd: Tomorrow, and tomorrow
No fail merges today, no fexts or flubstitutes. Just flubs, subbed by a pantheon of old hands. I think everyone represented has been active here for at least five years, save perhaps our first, anonymous submission!An anonymous Aussie shared a snippet of source from the ozpo website. Notice that even their "suggested example" doesn't fit."Australia Post's most public of many rule mismatches in their n-number of systems chained together.The description text field on the printed label? Sure, there's space for over 900 characters."
CodeSOD: Status Games
There is an entire category of WTF rooted in not understanding what HTTP status codes mean. As a quick refresher, the basic pattern is:
Modular Dependencies
Many years ago, Valts and his spouse both worked for the same company. The company had an ERP system that started its life as a product back when ERP systems were novel and new. It was written in Delphi, and it was huge- so huge that the company needed to hack the Delphi linker to handle its size.Well, their company got swallowed up by Initech, and after the buyout, things changed. Valts left, and a few years later so did his spouse. But they kept in touch with their colleagues, so it was over dinner that former co-worker Viktorija related the recent disaster she had stumbled across.Viktorija's day started when she needed to modify one of the "Business Objects". This product started its life well before ORMs were a common tool, and someone had taken to inventing it in house, wrapping around Delphi's TDataSet object and plenty of hard-coded SQL strings. What she needed to modify was just a minor validation rule, which she quickly changed and tested, and was quite happy with.While making the change, she also spotted a hard-coded SQL string that was just... badly formatted. Since it made the query harder to read, and since she was right there, Viktorija added some spaces and breaks and generally made the formatting readable. She committed her changes and went on to the next task for the day.The CI jobs failed on her commit. But they failed in a completely unrelated module, which nothing she had changed should impact. Nothing about a changed validation rule even applied to that module, and the errors were about database access- nothing in Viktorija's change should impact database access...... well, aside from modifying a query.Viktorija revisited her changes, and noticed that there was a "getter" function to retrieve the value of the query string. She had assumed that was just for debugging purposes, but when she CTRL+Fed through the broken module, she saw that it was fetching that string. And then it was mangling that string.At some point, someone said, "Code reuse is always good, and since SQL queries are code, I'll reuse it!" So they fetched the query string from the Business Object Viktorija had changed, and then did a series of operations like "delete the substring from character 160-173" and "insert this substring at position 57."By altering the whitespace and formatting, Viktorija had broken that code. It was easy enough to make the tests pass- all Viktorija had to do was revert that part of the change. Unpicking the string mangling and trying to make the system not a disaster was a much larger challenge.Upon learning of this story, Valts and his partner were quite happy to have left- and felt that Viktorija should go home with an extra bottle of wine after dinner. [Advertisement] ProGet's got you covered with security and access controls on your NuGet feeds. Learn more.
CodeSOD: Teaching Programming
Gracie had a friend who was interested in becoming a teacher. To get admitted into a teaching program at the local university, the friend needed to take an admissions test, to prove they were teaching material.Said friend tried to retrieve their test results, and the page glitched out. Gracie, who was handy, offered to see if she could identify the problem- likely an ad-blocker extension or something was breaking a script. A few minutes in the browser debugging tools, however, showed that the script came pre-broken.
CodeSOD: Easy Reader Version
Ensuring you code is readable is arguably one of the most important things you can do after ensuring it is correct. The real question is: readable to whom? Because apparently, some people have odd ideas about readability. Like Evan's co-worker.
12345678910...