Senior engineer with 12 years of experience in Ruby on Rails. I'm a strong generalist who quickly builds deep understanding of codebases, systems, and product context.
I care about quality, reliability, and maintainability, and I particularly enjoy refactoring existing code improve those aspects. I aim to solve immediate problems in ways that also support the broader product vision.
I consistently look for opportunities to reduce friction, improving the experience for both users and developers. For example, I built https://github.com/MaxLap/activerecord_where_assoc, a Ruby library with over 1.5 million downloads.
It is very funny to me that the sibling comment calls this "a very broken setup" and for you "it doesn't sound like a big deal".
It's all about perspectives, or you really just never had to deal with it.
The happy path ain't a big deal. But think of the unhappy ones:
* What if a server gets rebooted (maybe it crashed) for any reason anywhere in the process. Maybe you lost internet while doing the update. Were you still dropping tarballs? Did the server get it? Did it start with the new version while the other servers are still on the old one?
* What about a broken build (maybe gem problem, maybe migration problem, may other). All your servers are on it, or only one? How do you revert (push an older tarball)
A lot more manual processes. Depends on the tool you had. Good tooling to handle this is more prevalent nowadays.
I use Kubernetes for almost everything (including my pet projects) and I see the value it brings, even if for increased complexity (although k3s is a pretty good middle ground). But none of these things you mentioned are unsolvable or require manual intervention.
> What if a server gets rebooted
Then the rsync/scp would fail and I would notice it in deployment logs. Or it should be straightforward to monitor current version across a fleet of baremetal.
> Maybe you lost internet while doing the update
True, but even Ansible recommends running a controller closer to target machines.
> What about a broken build
That's what tests are for.
> maybe migration problem
That's trickier, but unrelated to deployment method.
Never said they were unsolvable. You asked for elaboration about pains of back then before lots of the tools most take for granted existed. You seem to think we are talking about massive problems, but it's more about a thousand papercuts.
> What about a broken build. All your servers are on it, or only one?
The ones you pushed the image are on the new image, the ones you didn't push the image are on the old image.
> How do you revert (push an older tarball)
Yes, exactly, you push the older version.
The command pushes a version into the servers. It does exactly what that says. There's nothing complicated to invent about it.
All the interpreted frameworks use the same semantics, because it works extremely well. It tends to work much better than container orchestration, that's for sure.
> A lot more manual processes.
It's only manual if it's not automated... exactly like creating a container, by the way.
For those unaware, Ruby blocks (and procs) are more flexible than anonymous functions as most language implement them. The article briefly goes over that, mentioning that lambda (regular anonymous functions) and procs (blocks) don't treat execute `return` the same way. There are also particularities with regard to next (continue) and break.
Flexible in a way, sure. But non-locality is generally a bad property, not good. Adding it is a workaround for all the enumeration methods using blocks in a way that makes people think they're weird looping syntax instead of a fundamentally different idea.
People want to do early returns from looping over a collection, so take the easy solution of adding more messy language semantics instead of finding a semantically simple solution instead. (For that matter, have fun working out the semantics of break and next when used in a block that isn't an argument to an enumeration method. How do you as a method author opt in to distinguishing between the two after yielding to a block?)
This is generally the case with Ruby everywhere. Why does that thing have an edge case with weird semantics? To work around the edge case with weird semantics somewhere else. It's all fine if you want to just try things until something works. But if you want to really understand what's going on and write code that's a first-class participant in the language features, it gets really frustrating to try to deal with it all.
For my (buggy, unfinished, languishing without updates) prototype Ruby compiler, lambda and proc (and blocks) are all implemented nearly the same way, with the exception that for proc/blocks, return/break/next will act as if having unwound the stack to the scope where the proc was defined first (or throw an error if escaped from there).
The distinction is very obvious when you think of it in that way - a proc acts as if called in the context/scope it was defined, while a lambda acts as if called in the scope it is called in.
> How do you as a method author opt in to distinguishing between the two after yielding to a block?
You don't. A block acts as a proc, not a lambda. If you want lambda semantics, then take a lambda as an argument - don't take a block/proc and be surprised it doesn't act like something it isn't.
Given that you completely ignored what I said I wanted to do and gave an answer for some other question, I'm pretty sure it's more complicated than you think.
I want to write a method that takes a block and distinguishes between next and break, exactly like the methods of enumeration do. It's obviously possible because a super common interface does it.
Last time I looked, that interface does it by being written in native code that interfaces with the interpreter. That is, it's not part of the language semantics. It's a special case with weird rules unlike what anything else gets to do.
Or at least it was. Maybe the language has actually made it accessible since then, but I'm not optimistic. That's not the ruby way.
Your sentence structure did not at all make it clear that "the two" referred to next and break rather than proc and lambda.
Assuming you don't care whether a "next" was actually called, but only whether you've exited the block and whether or not you exited it via a break, you can do this check in a number of ways, but it I will agree it's a bit dirty that sometimes you do need to rely on standard library functionality rather than language constructs if you want to do these things.
Here's one way of doing it, since "break" within a Fiber triggers a LocalJumpError:
def next_or_break(&block)
Fiber.new(&block).resume
:next_or_end_of_block_reached
rescue LocalJumpError
:break
end
p(next_or_break do next end)
p(next_or_break do break end)
> but it I will agree it's a bit dirty that sometimes you do need to rely on standard library functionality rather than language constructs if you want to do these things
You don't need to introduce a Fiber or rely on the standard library, you can just use the core language, leveraging the fact that code in ensure sections gets called even when you are returning "past" the calling method.
(You may need a dirty catch-all rescue so you can set a flag before reraising that lets you distinguish "bypassing direct return by exception" from "bypassing direct return by break or proc-semantics return", but that's still the core language, not standard library.)
Effectively blocks are self-contained chunks of code. You can change things around them, but you can’t change how keywords work inside them. Because you’re crossing a method boundary when you call a block you’re not able to access next and break. (Or capture return.)
Ruby is defining scope here and C methods are not limited by the language they define.
Exactly. You can leverage some of the standard library methods to figure out if a block did a next or exit normally vs. did a break, by e.g. abusing Fiber's (there are probably other ways too), and that might feel a bit dirty, but those are also part of the language.
I think it'd be nice if Ruby language constructs offered a few of the building blocks used by the standard library so that you could implement more of the standard library without dipping down into some low level mechanism - for my prototype Ruby compiler a priority was to implement as much as possible in Ruby, and there are certainly more things in the core classes that can't be done in pure Ruby than I'd like.
But it's not that much, and while there are plenty of warts, the inconsistencies are often smaller than people think.
It seems you're pretty upset about your experience with Ruby. I'm sorry that's been the case for you.
However, in Ruby blocks aren't just about flexibility, more importantly they're about generality. They're not there to resolve an edge case at all (Ruby also has keywords for loops). They're a generalization of control flow that is just slightly less general than continuations. In practical use they provide an alterative to Lisp macros for many use cases.
These were some of the problems Matz was trying to sort out with his design of Ruby--creating a language that was fun and easy to use for day-to-day programming, with as much of the meta-programming power of Lisp and Smalltalk as possible. Blocks are one of his true innovatations that came from trying to balance that tension.
Blocks in Smalltalk (to my understanding) are closures. Blocks in Ruby are closures that also bring the call stack they were created in with them.
One way to think of about it is this: anonymous functions as originally implemented in early Lisps are code as an object, closures are code with its lexical environment as an object. You can think of a Ruby block as code with its lexical environment and its call stack as an object.
So they don't just handle return differently than closures, they have access to the call stack of the site where they're created like a continuation. This is why they handle return differently, but this is just one of the things that falls out from that. It also comes with other control flow features like "redo", "retry", "next", "rescue", "finally", and others. These are all call stack control (control flow) conveniences, just like return is. All of them can be thought of as being abstractions built on top of continuations (just ask a Scheme hacker).
Originally Ruby was basically a Lisp without macros, but with continuations, a Smalltalk like object system and a lot of syntactic affordances inspired by Perl, and other languages. Blocks are one of the conveniences built on top of the Lispy semantics.
Note that I'm explaining how blocks work as an abstraction (vidarh below explains how they work as a concretion, as implemented in MRI).
Yes Smalltalk has continuations. So it can do all of those things as well. But I don't think they're explicitly tied to blocks like they are in Ruby. This really isn't a problem for Smalltalk since it's not as syntax oriented as Ruby.
The invovation is to have those features tied to convenient syntax.
I would say more broadly the innovation was two fold: 1) to make these features available in a syntactic form that would seem more familiar to programmers and 2) the powerful insight that when combined with Smalltalk style meta programming you can have a language that on the surface seems very conventional but underneath is just as powerful as Smalltalk or Lisp.
Although I would say he didn’t get 100% there although that this point Ruby isn’t too far from that.
These are ideas that I think are worth trying to take even further. In fact, I’ve been experimenting with that.
I agree blocks are closures. But they’re call stack aware closures. Which gives them _some_ of the power of continuations.
I’m also sure you’re right that they’re not implemented using continuations. However, my understanding is that Ruby was originally conceived as a language with continuations. I’ll see if I can find a reference for that. But from what I recall reading in a blog post from someone who was at a programming language conference in 1997 when Matz introduced the language that’s how he described it.
If you create a block deeply within a cascade of nested function calls, nineteen activation levels deep, and return that block out of all those nestings, is it still aware of the nineteen levels that have terminated, and to what purpose/benefit?
What example Ruby code would break without continuing access to the dynamic scope that has terminated, rather than just the lexical scope?
> If you create a block deeply within a cascade of nested function calls, nineteen activation levels deep, and return that block out of all those nestings, is it still aware of the nineteen levels that have terminated, and to what purpose/benefit?
A block cannot be returned, because it is not an object; it has to be used to create a Proc object with either proc or lambda semantics to be returned.
With lambda semantics, its just a closure, doesn't care about the dynamic scope, and returns immediately to whatever calls it with return/next, or returns from the calling method with break.
With proc semantics, it does retain the connection, and return or break will result in an error when those scopes have terminated, but next will still return to the caller. (You don't generally want to return a proc for that reason, the use for procs is passing down a call chain, not returning them up.)
OK, so we have gone from "blocks are continuations!" to "blocks (by themselves) are a kind of downward-funarg-only thunk!" (that can be used to specify the code part of a lambda or Proc, which are not continuations either).
No, return exiting the enclosing function scope from a block is the special case that's there to resolve an edge case.
Blocks should be nothing special. They're anonymous functions that capture the environment mutably. The only new part is all the special bits added to handle the weird edge cases that they're trying to pretend don't exist.
I disagree that it's just there to handle edge cases. It's a useful generalization.
I think the "Building an intuition" section of my blog post[1] makes a good case for that.
When dealing with loops, you have 3 nested constructs interacting: a wrapping function, a loop statement and the loop's body; and you have 3 keywords to choose where the flow of the code goes.
return returns from the wrapping function
break leaves the loop statement
next / continue leaves loop's body
When dealing with blocks or anonymous functions, it's instead 3 nested "functions" that are interacting: a wrapping function, a called function and an anonymous functions (or block).
Ruby's blocks, let you use the same 3 keywords to choose where the flow of the code goes.
return returns from the wrapping function (ex: my_func)
break returns from the called function (ex: each, map)
next returns from the block
Quite consistent. But since we are talking about functions instead of statements (loop), return values are also involved. Allowing both break and next to provide a return value fits well in that model and is quite useful. The 3 keywords are basically return, but they have different targets.
> No, return exiting the enclosing function scope from a block is the special case that's there to resolve an edge case.
What is the edge case? It seems to be there so that Ruby enumeration methods can provide the behavior expected of looping statements (which is kind of necessary if you want the looping statements to just be semantic sugar for enumeration methods so that they can work correctly with any enumerable.)
> have fun working out the semantics of break and next when used in a block that isn't an argument to an enumeration method.
The semantics are:
* next returns from the block, optionally taking a return value that will be returned as the result of the function (this is identical to the behavior of "return" from a Proc with lambda semantics)
* break returns from the function that yielded to the block, optionally taking a return value that will be returned as the result of the function (EDIT: deleted comparison to returns from a Proc with proc rather than lambda semantics here, because it wasn't quite accurate.)
This is, incidentally, also exactly the semantics when they are called from a block passed to an enumeration method, there is no special case there.
> How do you as a method author opt in to distinguishing between the two after yielding to a block?
If control returns to your method, it was a return from the block, either via return (from a block with lambda semantics), next, or running through the end of the block. If, OTOH, break is called (or return from a block with proc semantics), control won't return to the calling method normally, but code in any "ensure" sections applicable will be run, which can be used to identify that this has occurred, and even to override the return value provided by the break.
The simplest possible function illustrating this:
def how_exited
yield
direct = true
return "next, completion, or lambda return"
rescue
direct = true
return "exception"
ensure
return "break" if not direct
end
> How do you as a method author opt in to distinguishing between [break and next] after yielding to a block?
I don't use Ruby much lately, but if I yield to a block which calls break, control will pass to the code following the block (and not back to my method). If the block calls next or simply finishes, control passes back to me though I cannot know if next was called or not (but do I care? I can go ahead and yield the next element of a collection either way)
> But I can't even close an open file handle after a break.
Yes, you can. That's what ensure (the Ruby equivalent of "finally") is for. Or, better using File.open with a block where you do the work, which uses ensure under the hood.
Yes you can, you use a finally clause around your call to yield, as you would already do to handle possible exceptions happening in the block. So no extra consideration is needed.
In Ruby, you always use the global name (with caps) that normally matches the library with possibly some nesting. (exceptions exists, but its also possible to add globals in Python)
Unless you are talking about include, but thats for mixins, which are snippets of reusable coee you can add to you class.
It doesn't feel at all as dirty as `from blah import *`.
I'm no expert here.
You mention "either side" as if the light came from both the left and the right. But I think the light would be coming along a circle all around the sun depending on exact position.
So the trick here is that if you are at the focus point, you get all that light in a small area "for free". But if you try to catch the light on the way, you now need to catch eveywhere in a whole massive circle, which is basically impossible, so you only catch a minuscule amount of the light. And then have to deal with interferometry.
The article argues that most source available licenses also provide more freedom than only reading the code, such as using it and modifying with some limits.
But "source available", english-wise, sounds like you can only read it.
This pushes some to avoid the term and say open source since that's much closer to their what the license does, even if they are not exactly open source.
The argument is that "fair source" would be a much clearer term for most of the "source available with extra freedoms" stuff, and I agree.
Personally, I don't think this is a valid case of enshittifying. Products that you pay for that loses features or break or become more painful to use are enshittifying.
A free feature that stays free but requires you to make a free account (no credit card needed), I can see at least one very valid reason: if the feature heavier than a simple page (which is the case here), then it's an open door for DDOS attacks. Being able to track and ban/block the users that appear to participate in such an attack is totally valid.
The alternative is having to do captchas and the like to use those features anonymously, which is a pain both for user and for the devs/UI, and does feel more like the overall enshittification you are mentionning (even if it's a valid reason)
> The alternative is having to do captchas and the like to use those features anonymously
This is not the case. You may have noticed that Google Search, Bing, etc. don't require login or captcha to do a search. Billions of people use this search daily. And yet, they will throw a captcha at you, or even just say "you're a bot, stop bothering us" whether you're logged in or not, if their signals have detected what they consider abuse.
Clearly, their signals are not as naive as "anonymous user, require captcha / logged-in user, no checks required". Preventing DDOS != requiring login.
They like you logged in because they can add more data to their verified user identity and activity datasets and sell them for more money. They already make enough money to run the service despite all the anonymous usage, but they'd like more money, you see.
Github managed to offer anonymous search for 16 years before one day Microsoft took it away. Do you think it was due to DDOS attacks, or do you think it was a power-play to attract more sign-ups and logins?
> They already make enough money to run the service despite all the anonymous usage, but they'd like more money, you see.
How mighty of you, a freeloading user in this specific situation, to assert Github has made "enough" money and therefore should offer you services at their own expense... you know, because you want it and therefore are entitled to it.
> Github managed to offer anonymous search for 16 years before one day Microsoft took it away. Do you think it was due to DDOS attacks, or do you think it was a power-play to attract more sign-ups and logins?
So what's the issue here, really? Make a free account and move on with life. Or clone the repo and search it locally if you need to. Or decide to take some principled stance and refuse to work with projects hosted on Github. It's your choice.
It’s Microsoft, one of the most successful companies in human history. A poorly formed moralistic argument about “entitlement” is absurd. They certainly feel entitled to every aspect of my life, as do most other Fortune 500 companies, I think I can safely desire not needing to log in during a damn search.
So again, because Microsoft has more money than you do, it entitles you to their services for free?
Where else in life does this logic apply?
Perhaps you waded into a conversation without even understanding the core complaint. You can search on Github without a user account, entirely for free. However, they do not provide context-based code search to non-users, despite it still being free.
If for whatever reasons you cannot possibly be bothered to create a free user account out of some irrational fear Github will sell your codebase search history to advertisers (laugh out loud, literally), then you don't get to use that feature. Clone the repo and search it yourself, or find a different deep-pocketed service that lets you mooch everything for free.
tldr; Why are freeloaders always the loudest complainers?
> The general idea of imposing more user-friendliness on very large corporations is not a bad one.
This is not a "user-friendliness" issue by it's very definition. The OP is not even a user!
> "more money than you" is a pretty crazy strawman of the actual comparison they made.
Perhaps you didn't read the conversation. The parent literally made the argument that Microsoft (ignoring that Github is a separate company) has plenty of money and therefore should provide this service for free even to non-users.
The service is free. A user account is free. It doesn't get more simple than this.
The naivety to believe the lack of a Github account somehow safeguards your browsing data is as hilarious as it is sad. Further, believing the creation of an account and searching code repositories somehow results in more ads is beyond hilarious.
This entire thread is pure insanity. Life doesn't need to be this difficult people. Create a throwaway account if you are so worried... or find some other service. You are not owed anything by Github - yet despite that they have made it trivially easy to benefit from their services at no cost to you.
> This is not a "user-friendliness" issue by it's very definition. The OP is not even a user!
They don't need an account to be a user, as you seem to acknowledge later in your comment: "The naivety to believe the lack of a Github account somehow safeguards your browsing data"
> Perhaps you didn't read the conversation. The parent literally made the argument that Microsoft (ignoring that Github is a separate company) has plenty of money and therefore should provide this service for free even to non-users.
They said "one of the most successful companies in human history".
That has nothing to do with the parent's amount of money. It's not a human-comparable amount of money.
If the company had ten million dollars the parent wouldn't be making the same argument about size.
> This entire thread is pure insanity. Life doesn't need to be this difficult people. Create a throwaway account if you are so worried... or find some other service. You are not owed anything by Github - yet despite that they have made it trivially easy to benefit from their services at no cost to you.
I have an account. That doesn't change how companies should work. And I find no "difficulty" in having a little discussion.
> You are not owed anything
Yeah I am. They make mass market money and they use public infrastructure. If the population wants to impose rules on them, the population gets to. Like forcing them to pay taxes. That's money they owe me indirectly.
I don't get to make the decisions on my own, but I can say if I think a theoretical rule would be good.
For the last seven months, Google has pushed every non-login search from my house network through a captcha; the image captcha is typically five to infinite repetitions. Audio captcha works after a single run-through, except that it is frequently "unavailable" now.
I don't know why. Google won't tell me. They just started doing the same for YouTube: "Please login because we have detected malicious behavior from your network".
I know I'm not DDOSing them; I can see all our network traffic. They're just encouraging me to avoid using them.
You're probably blocking ads or blocking tracking in some fashion and denying them signals their naive models use to evaluate whether you're bot-or-not. It could be somewhat intentional but I'd lean towards it being an edge case they just don't care to address.
In my case, they block me on IPv6 since I use a Hurricane Electric IPv6 tunnel. It certainly used to be the case that my IPv6 connectivity was better and more performant down the tunnel than using my ISPs native IPv6. I have no idea what the situation is today.
My solution was to use a filtering DNS that always returns no AAAA records for domains ending in google.com. This works great and essentially solves the problems. I have to do the same for various netflix domains as well.
I'm dreading having to switch over to native IPv6 -- I don't even know how many /64s will be allocated to me (and how stable they will be).
I feel this. Recently _some_ company, I have no idea which, has decided my IP is malicious and refuses to serve my requests. This has effectively banned me from a few websites, including a government service I pay taxes for.
As far as I can tell, mainline web search engines are mostly serving cached/canned responses these days. They get updated periodically but it's not the same as the late 90s or early 2000s when every search was run against large-scale content indexes. You can occasionally stack keywords or form unique enough queries that you force the engine to do real work, but getting this right seems to get harder and harder over time and their pool of content that's indexed seems to be broad but shallow now.
GitHub code search is still doing real searches and so is much more expensive to run.
> You may have noticed that Google Search, Bing, etc. don't require login or captcha to do a search.
Are you saying you want adds in GitHub search's results? Google, Bing, etc. make money showing you adds. Adding barriers of entry is much less in their interest. Their budget to optimize the search engine is likely much bigger than GitHub's one.
The entire point of Google Search is to take in everyone, serve ads, and drive people to use other Google properties. Every user that has to jump through hoops in order to access Google Search is a net loss for them.
GitHub doesn't really care all that much if random anonymous users can use their search. Anon users can view source trees, wikis, etc. and check out code, which is more than enough for most people.
> Do you think it was due to DDOS attacks, or do you think it was a power-play to attract more sign-ups and logins?
I think you and I don't know anything about what's going on there internally. I'm usually quick enough to assume the worst about actions Microsoft (of all companies!) takes, but even former GitHub employees have commented here that the new search system is much more resource-intensive than the old, and bots and scrapers were causing real problems. I choose to believe people who seem credible instead of playing the cynic and assuming everything is done with evil intentions and that everyone is lying to me.
Sure, they could build a big sophisticated system to figure out who to serve CAPTCHAs to, or who to outright ban, but why spend the time and money on that when they can just require a login, and the people they care about won't really care.
And sure, this move very well might drive some new signups. Maybe that's a net win for them. So what?
It raises the question of what will they hobble or take away next to fatten their bottom line. Will they continue to be good custodians of the real treasure, which is the projects that they host?
You may remember SourceForge was a popular hosting site. Ultimately, what caused a mass exodus was that they decided to let malware creators pay them money to wrap around the installer packages of the software they were hosting.
If you're not hosting your own project, there is always this risk. Question the motives of someone who offers to host your stuff "for free" ... and then alters the deal some point down the line.
Of course, they could have kept the old search (without advanced filters) open, but there is at least a sensible explanation why the new search requires being signed in.
Senior engineer with 12 years of experience in Ruby on Rails. I'm a strong generalist who quickly builds deep understanding of codebases, systems, and product context.
I care about quality, reliability, and maintainability, and I particularly enjoy refactoring existing code improve those aspects. I aim to solve immediate problems in ways that also support the broader product vision.
I consistently look for opportunities to reduce friction, improving the experience for both users and developers. For example, I built https://github.com/MaxLap/activerecord_where_assoc, a Ruby library with over 1.5 million downloads.
Open to full-time and part-time engagements.
reply