There’s also a typography element to formatting source code. The notion that all code formatting is mere personal preference isn’t true. Formatting code a certain way can help to communicate meaning and structure. This is lost when the minimal tokens are serialized and re-constituted using an automated tool.
And I'd add that typographers go out of their skin to typeset tables and formulae so that everything is aligned and has proper spacing. For centuries this was done manually because it it important, even though an outsider cannot notice it.
(That said, it must be possible to make a more sophisticated formatter for the source code too.)
Yes. In Python, black formatter consistently breaks SQLAlchemy queries in an unreadable way (e.g. splitting conditions over multiple lines when it's not really necessary and makes reading harder).
For C++ clang-format does things like that all the time as well. Of course it has no idea what semantically belongs together on the same line or not. I wish the C++ world had settled on some other standard linter.
clang-format is probably the worst of the autoformatters. They tried to get fancy with a sort of global optimisation algorithm but in practice it's buggier and uglier than the classic Prettier algorithm which is elegant and generally works very well. It's also way less diff friendly.
I wouldn't draw any conclusions about autoformatters from clang-format.
> Some of us even align other parts of our code, such repeated inline comments
> Now, the arguments block forms a table of three columns. The modifiers make up the first column, the data types are aligned in the second column, and the names are in the third column
These feel like pretty trivial routines that can be encompassed by code formatting.
We can contrive more extreme examples, like the for loop, but super custom formatting ("typesetting") like that has always made me feel awkward, feels like it givesicemse for people to use all manners of arbitrary formatting. The author has some intent, but when you run into an inconsistent code based with lots of things going on, the variance doesn't feel informative or helpful: it sucks and it's a drain.
What's stored is perhaps more minimal, some kind of reference encoding, maybe prettier-ifies for js. The meat of this article to me is that it shouldn't matter: the IDE should let you view and edit as you like:
> Everyone had their own pretty-printing settings for viewing it however they wanted.
> A C argument declaration is made up of modifiers (register, const), a data type (char *), and a name (from).
Now explain a declaration like "char *argv[]"...
> We’ve also re-set the data type such that there is no space between char and * - the data type of both of these variables is “pointer to char”, so it makes more sense to put the space before the argument name, not in the middle the data type’s name (update: it should be pointed out that this only makes sense for a single declaration. A construct like char* a, b will create a pointer to char, a, and a regular char, b).
Ah, yes, the delusional C++ formatting style. At least it's nice that the update provides the explanation why it should be avoided.
My $0.02: Don't throw away a perfectly good mental model because of a compiler ideosyncasy. Just treat it as a special case and use a linter against stuff like char* a, b.
You also don't think about dollars differently than other units, just because the sign goes before the number.
Caring for typography but blindly bending to dubious programming-language convention feels really like putting efforts on the wrong starting point though.
What’s the point of such an heavy obfuscation of the intend, really? Let’s take the first example.
If we are fine with the "lengthy" register, why not use character in full word? Or if we want something shorter sign would be actually semantically more on point in general.
What with the star to design a pointer? Why not sign-pointer? Or pin for short if we dare to use a pretty straightforward metaphor, so sign-pin. Ah yes by the way, using "dot" (.) or "dash, greater than" (->) is such a typographical non-sense.
And as a side note *char brings nothing in readability compared to sign-pin-pin. Remember that most people read words or even word sequences as a whole. And let’s compare **char to something like sign-pin-back-5.
What with strcpy? Do we want to play code-obfuscation to look smart being able to decode this pile of letter sequence? What’s wrong with string·copy* or even stringcopy (compare photocopy)? Or even simply copy? If we want to avoid some redundant identifier without relying on overriding through argument types, English is rich in synonyms. For example duplicate, replicate, reproduce.
Various parentheses could be just as well optional to ease code browsing if proper typography is already on place, and English already provide many adverb/preposition that could replace/complement them into a linguistically more usual counterparts.
Speaking about prepositions, using from and to as identifiers for things which would be far more aptly described with nouns is really such a confusing choice. What’s wrong with origin/source and destination/target? It’s also a bit counterproductive to put the identifier, which is the main point of interest, at the very end of it’s declaration statement.
Equal for assignment is just really an artifact of more relevant symbol like ← or ≔ because most keyboard layouts stem from disastrous design. But using an more adequate symbol is really pushing for unnecessary obscured notation.
Mandatory semicolon to end a statement is obviously also a typographical nonsense.
If a parameter is to be left blank in for, we would obviously be better served with a separate control-flow construction rather than any way to highlight it’s not filled in that employ.
So packing it all:
duplicate as function ⟨
requiring (
origin as sign-pin-register,
destination as sign-pin-register
)
making {
save as sign-pin
save assigned origin
destination-pin assigned origin-pin until ( zeroized,
whilst [
origin-increment,
destination-increment
wrought ]
done )
return save
made }
built ⟩
Given that in that case the parentheses and comas are purely ornamental, the compiler could just ignore them and would have enough information with something like
duplicate as function
requiring
origin as sign-pin-register
destination as sign-pin-register
making
save as sign-pin
save assigned origin
destination-pin assigned origin-pin until zeroized
whilst
origin-increment
destination-increment
wrought
done
return save
made
built
Or even
duplicate as function requiring origin as sign-pin-register destination as sign-pin-register making save as sign-pin save assigned origin destination-pin assigned origin-pin until zeroized whilst origin-increment destination-increment wrought done return save made built
(author here) It only uses Sinatra because I happened to know it and needed to bootstrap the low-level stuff. I don't know if it needs to be based on Sinatra in the long term - it should probably just use Rack.
Thanks for the reply. Interesting that you mention that it should just use Rack. It reminds me of a time a number of years ago where I was on a mission to use a framework which did not have a million and one dependencies, but just Rack.
Not sure you have come across https://github.com/Ramaze/innate (I am showing my age now :) . It is the core of the Ramaze framework. I really liked the philosophy of that project. Its a shame it never caught on though.
You can use a static analysis tool to check Ruby types ahead of time (still not a compiler) or provide information for tooling.
Alternatively you use them at runtime to check the correctness of data, which I don't think you can usually do with say Typescript where the typing information is for the most part compiled away[1].
1. I may be out of date on this but when I last looked at runtimes that could take Typescript directly they just threw the typing away. You just didn't need to use the tsc compiler first.
I didn't want the code to be All Rights Reserved, so I chose the best license I could find that communicates my desires - I assume that's what most people do when choosing a license?
I'm OK if a "semi serious business" don't want to use my software.
Author here. The framework does require setting stuff up. RSpec is not one of those things. It's the testing library you will use if you use this framework. I didn't create a lot of flexibility in the framework. For example, if you don't like RSpec, you will not like this framework :)
You may want to examine the docs more closely. There are plenty of conventions and very few that can be circumvented.
But day one of a new framework is not going to compete with Rails. Sorry!
To be honest, I love this framework (so far from what I've seen) but I do hate the choice of RSpec. I say this as someone who used RSpec for like a decade after it was first released. I "get" it. I have contributed code to it.
Minitest (which is bundled with Ruby these days) is Good Enough™. It doesn't require you to learn a new DSL. Everything is just Plain Old Ruby. The use of RSpec these days—IMO—is just cargo-culting forward what was the right decision from 10+ years ago. Having an pseudo-English style interface for testing isn't worth having the additional dependencies nor the mental overhead of needing to know how the RSpec syntax is actually mapped into Ruby concepts.
I'm not asking you to change it, you're welcome to have a different opinion than mine. But I am curious if you have strong reasons for requiring it. Particularly because MiniTest seems to be well-aligned with the rest of your design philosophies, unlike RSpec.
I thought hard about this decision. Every time I use MiniTest, I end up wanting a bit more that RSpec has and then switching to it. I also have been surprised over the years that the `expect(x).to eq(y)` seems to be relatively intuitive to people, despite the fact that it doesn't seem like it ought to be.
Do you mind elaborating on what more you end up wishing you had?
Most of the additional features RSpec adds seem worthwhile but just end up being more complicated/confusing in the end, in my experience. Shared contexts/examples, for example. Others like let blocks should… just be methods.
I will say the change in… I think it was RSpec 3(?) to the expect(x) syntax was a hugely positive change, particularly in not having to monkeypatch the world.
The biggest thing is the mocking system. MiniTest's feels so difficult to use.
I also like creating custom matchers vs. creating my own assert_* methods.
I would agree that many features of RSpec are, honestly, bad: shared examples, shared contexts, etc. Excessive use of let! and let, plus the predicate matchers are all just really confusing to me.
I actually thought about patching the RSpec gem to remove the features I didn't like :) Might still consider it heh
The mocking thing actually touches on another point of frustration for me. I think the design of Rails ends up causing people to reach for mocking way too often in order to test things. At a glance I think Brut should avoid a lot of this by having things just be plain old Ruby objects.
I have dealt with countless Rails projects where testing things conventionally was difficult or impossible so mocks/stubs had to be used everywhere (controllers are the worst offenders here). When you start digging in to what's actually being tested, you find that the tests express little more than "this method is written the way it's currently written" rather than actually testing behavior.
Good tests should do three important things: catch bugs early in development, prevent regressions, and allow refactoring. Overly-mocked tests not only end up doing none of these but often actively work against these goals. They can't catch bugs because they reaffirm the current implementation rather than testing behavior. They can't catch regressions because any change to the code necessitates a change to the test. And they actively inhibit refactoring for both of those reasons.
All that is to say that maybe having a less-convenient mocking system is maybe a good thing :)
Also, since you're here, I want to say it also looks like your design encourages avoiding one of my other huge issues with Rails. I hate that ActiveRecord conflates the ORM layer with domain logic. This causes an antipattern where consumers of records (usually controller methods) pierce all the way down into the database layer by using AR methods and attributes directly. While convenient, this makes doing database-layer changes excruciating since your table layout is implicitly depended upon by pieces everywhere throughout the stack.
Better is to do what it looks like you suggest here: there should be an ORM layer that just exposes the database structure, and then you should layer domain objects on top of that which expose higher-level methods for interacting with persisted objects. If you change the database, you only need to change the mid-level layer. None of its consumers need to care that the underlying table layout changed.
From what I can tell so far I am very excited about Brut.
Yeah, that makes sense. Where I end up wanting mocks is when this happens:
1 - build first version of feature, all core logic in a class I can test conventionally
2 - logic gets complex, test gets complex
3 - Eventually, I need to create some layering, where the class from step 1 now delegates to other classes. The initial test is more like an integration test and gets harder to keep up
At this point, there is a camp that says I should be using dependency injection and inject null objects for the dependencies. I get that idea. I am in the other camp that does not want to make custom objects just to satisfy a test. A mocking system can do that for me. So that's what I would do - mock the dependencies. The "real" versions would be tested conventionally.
I definitely do NOT just start with mocking imaginary internals though - I guess that's a whole other camp :)
My opinion is, _my unit tests_ are to protect my code against unwanted changes. To that end, unit tests test a single unit. And everything is mocked. If I have to rewrite a method, usually I rewrite all of its unit tests. Which is fine. They’re easy to write or rewrite.
Fully mocked unit tests are then supplemented with fewer “full stack” tests higher up the pyramid.
> My opinion is, _my unit tests_ are to protect my code against unwanted changes.
This is just about the most bizarre take on unit testing I've seen in my 25-year career.
> If I have to rewrite a method, usually I rewrite all of its unit tests.
If you have to rewrite your tests every time you rewrite a method, you are entirely defeating the point of testing. What value you get from tests that only assert that the current implementation is equal to the current implementation?
You seem confused about what he was saying. I’m sure you are familiar with unit testing philosophy with your experience. Calling a function and expecting a response does test the behavior of the application, just at a lower level than a request- or multi-request level spec. When he says rewriting a method he is referring to changing the logic of it; a refactor leaves the tests unchanged. That shouldn’t have needed to be spelled out to such a senior developer.
Tests should not generally need to be rewritten unless you’re changing the externally-visible behavior API of a function or library (either the literal API, its effects, or its semantics).
If you’re changing the tests because you’ve changed the internal, non-externally-visible logic of your code, your tests are almost certainly providing you negative value.
Being able to refactor and re-run your existing test suite to ensure consistent behavior is possibly the important property of a good test suite.
> Fully mocked unit tests are then supplemented with fewer “full stack” tests higher up the pyramid.
Has the wrong priority. Full stack tests are the most valuable for shipping working software. Tests are enforcing contracts between components. Often I’ll see unit tests written for cases that do not exist. This is very bad. Testing at the boundary of your stack ensures the contracts that matter are enforced. Everything else is just making devs wonder “is this requirement actually real?”.
I used to be big on unit tests until I had to maintain a codebase for more than a couple years. I still use them, but mostly out of laziness.
> I actually thought about patching the RSpec gem to remove the features I didn't like
Hmm it all sounds like you'd end up bastardising Rspec into being Minitest::Spec?
If you really like things like `expect().to` it's much easier to add it to Minitest::Spec (I did it back then, since there's `_().should` it's like 5-10 lines) than removing all those features and suffering the rspec weight.
I agree with this. RSpec may be an extra dependency vs MiniTest but with MiniTest I'll almost always need extra plugins anyway for things like reporting, colour coding, etc.
MiniTest is almost good enough but not quite there on its own.
Author here - the example doesn't allow logins now to avoid abuse. I wasn't 100% sure I'd make it public and just decided today to do it, so it's not yet ready for just anyone to login.
You can run the site locally, or view pages on the site by going to https://brutrb.com/adrs.html and clicking those links.
I built a clone of it for Windows using Direct X in probably 1999/2000. I don't have a Windows machine, but I think Microsoft's obsession with backward compatibility means it probably still works:
It replicates the 2600 game, and adds two new levels (additional castle and maze), and two new (optional) objects, a candle that shows more of the labyrinths, and a shield that prevents the dragons from eating you.
Where you live matters. In the US there is a significant tax burden on exercising. Talk to an accountant. Also look into QSBS which can shield your tax burden if the company was small enough when you were granted stocks. Highly recommend you get informed before talking to a professional. Not all accountants understand this stuff.
It wants to act on GitHub on my behalf. Not clear why I should allow it to do that, especially when the only info about this app that is presented is that it will store my data on GCP.
Can you modify your oauth request to only ask for what permissions you need and/or itemize out what the app is going to do on my behalf?
It's worth mentioning that GitHub only allows us to act on your behalf in repos you install the app in. But when you install the app you'll see it's only requesting read permissions to metadata => it can't take any actions, either by itself or on your behalf.
https://naildrivin5.com/blog/2013/05/17/source-code-typograp...