HN user

saltedmd5

188 karma
Posts1
Comments43
View on HN

The power of DVCS is in that each developer who clones a repository has a fully-functioning local repository complete with history, which can be committed to freely and seamlessly merged into the upstream later.

As opposed to the traditional version control model where, e.g. every commit effectively requires a rebase against the remote and the history cannot be retrieved without a connection to the remote.

The "D" in "DVCS" is about having many copies of the repository, not about having _no_ central repository, which is still a core part of having an effective delivery workflow and very much encouraged by the baked-in concept of a default remote repository.

It's a distinction of technology, not of workflow.

This is really a common misunderstanding about what makes DVCS an effective concept.

This is why vertically-sliced product teams work - they minimise the communication and coordination overhead associated with Doing a Thing(TM), while layered or function-specific teams make everything take so much longer - particularly as the formal documentation and communication overhead grows enormously when different teams are accountable for different aspects of the same system. When a small team owns an independent(-as-possible) vertical slice, accountability is shared and the need for well-documented communication is largely mitigated.

AFAIK rebase always rewrites history. If you rebase your feature branch, you have at least rewritten the history of the local branch even if you then delete it and push to a new remote branch. The trunk will subsequently get a FF merge.

Rebase might give you a "simpler" end result in terms of what the history looks like but conceptually it is much less simple in terms of its mechanism and its implications (e.g. rebasing a branch with multiple contributors screws up the audit trail as it now looks like they made their changes at a different time and in a different context to when they actually did) than the idea of a graph with two branches and a merge commit.

I have seen teams with limited git experience switch from habitually rebasing public branches to accepting merge commits and suddenly cure a whole host of workflow problems.

If you can rebase directly onto a fresh branch (not something I've seen) then I am fairly sure that that's not part of your average workflow - establishing new branches every time you want to update from trunk comes with its own communication overhead too.

Rebase is not simpler in any context - rebase rewrites history, which, if branches have been pushed to remotes, then necessitates force pushes, which in turn breaks any other instances of the same branch. By using rebase to "keep history clean" you are largely undermining git's power as a DVCS.

Rebase as a tool is not inherently bad but it is definitely not simpler than merge - it introduces additional considerations, requires a deeper understanding of git for effective use and is a dangerous tool in the hands of people who do not understand what it is doing and in my experience most teams that are using it as a core part of their workflow are doing so for the wrong reasons (generally because of a fundamental misunderstanding of how branching and merging works in git and why it works that way).

The point is that returning an Optional gives you nothing above returning a null - in fact it increases complexity and makes calling code more error-prone for no gain.

If your contract says you return a Foo, you could be returning a null. If you change that to Optional<Foo> you could still be returning a null.

It's not what Optionals are for. They are just widely misunderstood and abused.

Thank you for calling this out. I have to fight this fight way too often.

Optional is really for functional code, not for "eliminating NPEs," because it doesn't do that - in fact it means strictly speaking you have to do additional checking.

The real purpose of optional is for allowing me to do things like `foo.first().orElseGet(other)`. `first` has to return an Optional rather than a null or it will blow up.

If you are doing `Optional.isPresent()` as a straight-up replacement for null checks then you are doing it wrong. If you want to avoid returning nulls and doing null checks, use polymorphic null objects.

Something isn't ironic just because you wouldn't expect it, something is ironic because it is the inverse of expectations.

Like if her name had been Lady Smallheart.

The problem is the idea that "layers" and "abstraction" come as a package. You don't need layers to create meaningful and useful abstraction - in fact more often than not they prohibit it.

And you certainly don't need layering within a single codebase - if I see one more codebase where everything is a FooController, FooService or FooRepository I just might go insane.

People abstract logic away from controllers only to dump it all into a massive procedural "service" class, which leads to no end of pain down the road. Once you stop seeing layers and start seeing a vertical dependency tree starting at the controller, the world gets a lot more rosy. Consumer-driven development helps here.

The truth is that the construction of CRUD web UIs _is_ 90% automated.

The web and storage frameworks do the vast majority of the heavy lifting - the application code is really just simple wiring and validation.

The trouble is that (in my experience, at least) many web shops and web developers enormously overcomplicate that application code; shoehorning enterprise "layering" into simple components, creating poor - and often unnecessary - abstraction. These things lead to grossly over-engineered solutions with bloated code and control flow flying all over the place.

So the reality is that what can be automated largely is already - we are just, by and large, pretty bad at doing the bit that isn't.

That being said I think there is a minimum level of effort, intelligence, and resilience required to take advantage of certain lucky opportunities if they present themselves.

Lucky you for having been born with willpower and smarts, then.

Actually, I think your comment is a simplification: there absolutely is a trade-off between money and satisfaction. That doesn't mean you have to become a starving artist to increase your satisfaction level - just that prioritising x means compromising on y.

That might mean working a lower-skilled job you can just clock in and out of so you can focus your efforts and time on other things you care about or even just applying your existing specialism in an industry/company that offers you more creative freedom at the expense of some compensation.

Re: first paragraph - incorrect.

The problem with "specs" is that you don't know whether they solve the problem until the solution is implemented and shipped. And even then, you have no real way of knowing what's meat and what's cud. So lots of time, money and people are thrown at solutions that - at best - are wildly bloated or inefficient and - at worst - completely fail to address the actual problem at hand.

The _need_ for specs is - even in large organisations - typically down to misplaced accountability. When you decompose the problem space into small pieces and give development teams a high degree of problem-solving autonomy _and_ accountability for production, the organisational disconnects that lead to the "need" (or, rather, the ill-conceived desire) for burdensome process and specification largely go away.

This isn't witchcraft - it's progress. It works; and it works in large organisations. You haven't witnessed it, so you don't believe me. If/when you do, I'd be willing to bet you'll be converted as I was.

But I'm sure I've given you plenty of reasons to double down on your skepticism.

Thing is, the need to formally define "behaviour" like that is a symptom of poor organisational/team structure. If you have multi-discipline teams with end-to-end accountability for a complete product (or vertical slice of a product) then your "spec" can be replaced with a problem statement and all of a sudden people can start focusing on whether the output solves the problem rather than whether it satisfies some arbitrary behaviour.

Your job as a software developer is to solve problems with code - not to take the thing someone else designed and make it go. That line of thinking just doesn't work.

Designing and building software are now the same thing. And that is only going to become increasingly true. Designing solutions in the context of modern systems inherently requires intimate knowledge of those systems and the capabilities of the technology being used to solve the problems. The "design/spec/build/test" pipeline is dying.

You can recognise that that's a good thing and get on board or you can be left behind.

A lot of the time it's not even really a compromise. This is why it makes no sense to debate about eventual consistency - the implications are entirely situational.

This kind of buzzword is harmful; it leads to overthinking, over-engineering, misunderstanding and a lack of focus on what matters, which is implementing solutions that are appropriate for the current context.

It's also a fallacy (or an artefact of enterprise thinking) that you need to either adopt eventual consistency or not. Modern systems are increasingly webs of loosely connected components and subsystems where some processes may be immediately consistent and others not. This is something that we take for granted most of the time (e.g. a search index is almost always "eventually consistent" and nobody would ever think to question that) until we start talking about the concept in an abstract/contrived fashion.

This depends on the definition of "right," though - if "close enough" really is enough, then "close enough" _is_ right.

"Right" doesn't necessarily mean "precise."

This is the crux of the problem with a lot of software engineering culture as it stands today - blog posts and talks frequently deliver abstract concepts that only really make sense to people who have already grasped/discovered those concepts.

The enlightened sit nodding along, while the unenlightened go away confused or misled. Positioned this way, half-understood ideas frequently do more harm than good.

TDD is a great example of this.

In this case, though, I think the article does a fairly good job - it highlights productive ways of thinking that will incite useful self-reflection ("Do I do this? Could I do it better?") rather than half-communicating ways of doing.

In addition to notes in other comments, vertical slicing helps to avoid a tendency for rigid layers to emerge (think the rigid and doomed-to-be-messy Controller -> Service -> Repository "pattern"), which ultimately helps lead to looser, more naturally composed component structures.

You can still have files and folders that cut horizontally across those modules (e.g. for things like logging, sending an email, etc. etc.) - in fact, organizing your hierarchy in vertical slices helps to _promote_ good isolation of those cross-cutting concerns.