Hacker News new | ask | show | jobs
by miki123211 2440 days ago
This is yet another example of the divide between wizarding and engineering[1]. When you're a small startup, what matters is the expressiveness of your language, and the ability do do a lot of things very very quickly. Type safety, performance, readability, those things don't matter. You're just a bunch of engineers who know the whole codebase inside out, you're pretty certain of what you're doing. In short, you're wizarding. If you grow big enough, this approach slows you down greatly, and you need to switch to engineering. You sacrifice some speed for making the codebase more understandable to a larger group of people, you can no longer assume everyone knows all the code, you write unit tests, need types and dislike metaprogramming because of the confusion it creates. This is why languages like Python, Ruby, Lisp or Smalltalk are amazing for small startups, but Java is what enterprises use. They're different ends of the wizarding/engineering spectrum. I wish there was a language that let you move gradually from one end to the other, exactly when you need to.

[1] https://www.tedinski.com/2018/03/20/wizarding-vs-engineering...

10 comments

> I wish there was a language that let you move gradually from one end to the other, exactly when you need to.

This is precisely what gradually typed languages — like TypeScript, Flow, and typed Pythons — solve!

I talked about this on Software Engineering Radio last week: https://www.se-radio.net/2019/10/episode-384-boris-cherny-on....

On that note, I'd include Erlang. It's not gradually typed, per se, but you can have a fully dynamic language (no type specs, no Dialyzer), a completely optimistic static analyzer for inferring types and warning where it's inconsistent (Dialyzer runs), and then you can add specs where needed to tighten up and improve what Dialyzer can catch, to basically be a fully static language.
It's relatively easy, but not free, to do this. I find that the erlang (and elixir) guides seem to be a bit scant on best practices to achieve this level of discipline, for example, wrapping all gen_sever calls in module functions and presenting a well-defined API for the genserver module (and possibly, even linting for no naked genserver calls) is not really explained in this light. Similarly guidance is not provided for wrapping enum module calls (since that similarly destroys typing information)
Yeah; it requires some rigor to do. My point was simply that it _can_ be done, and while the effort is high, it does allow you to move from pure dynamic language, to highly defined type checking.
Dialazer is pretty bad and not even close to a static language.
If you fully spec out your code, it's actually quite close. In a project we did that in, the only type errors we encountered were ones that a static system would not have caught either (due to their being caused by incoming data that did not conform to our type expectations; for instance, deserializing JSON to a specific type).

Without specs, it will assume every type is 'any()', unless it has information to infer something more stringent. For instance, if it sees you add 5 to it somewhere, it will instead assume it is a number. Etc. Even if in practice it actually is a list of some kind (and so that addition of 5 will fail). Which, yes, ain't great. Hence why I said it was a gradual transition; it will catch provable errors (i.e., if you call append on that same variable as above, it will note that there is no type that allows both append, and + an integer, and error), but leave plenty of things uncaught that could have been caught had it known the type in question (via a type spec).

That critique is a bit unspecific but I tend to agree.

I have heard good a stuff about "gradulizer" though. It uses a gradual type system instead of dialyzers success typing.

https://github.com/josefs/Gradualizer

TypeScript is perfectly this. (And other gradually typed solutions; TS is simply the most popular one.)

You have the madness of thousand of developers flinging code at the universe due to the easiness of browsers, JS, and npm.

This results in great speed, but not great quality.

When your project/company now wants quality, you keep your code but transition to types. (In OSS space, Angular and Yarn projects have both done JS => TS migrations of some form.)

Afaik, typescript is pretty bad in terms of catching some basic errors. Types are not enforced. A caller can change sync function to async, breaking the functionality downstream.
You can decide if they're enforced or not. That is part of how it is gradual typing.
Yes gradually typed languages are usually looser/more flexible about static typing.

> A caller can change sync function to async, breaking the functionality downstream.

I think you mean callee?

Are you taking about using a returned result? Because most languages permit ignoring return values.

If you want to check out promise use, check out https://tsetse.info/must-use-promises

Sorry, I did mean callee.

Consider

``` const myIntValue = f(); ```

This code will silently break when f changes from sync to async.

Right, but there will be an error where you consume myIntValue:

  const myIntValue = f()
  
  // Error TS2365: Operator '+' cannot be applied to types 'Promise<number>' and '2'
  const myResult = myIntValue + 2
  
  async function f() {
    return 42
  }
Typescript was exactly what I was going to mention in reply
It's not just about static typing, though. Macros, metaprogramming, being able to reach as deep as you want to, ugly code full of side effects, global state etc. All of those might actualy benefit you when your project is small, and they make development way faster (see Rails). Later, however, they're a definite impediment.
> When you're a small startup, what matters is the expressiveness of your language, and the ability do do a lot of things very very quickly. Type safety, performance, readability, those things don't matter.

I’ve never worked on a program so small that readability didn’t matter. I consider it a crucial ingredient of expressiveness and development speed.

Though your perspective could explain a few of the more atrocious code bases I’ve seen.

During exploratory programming I don't care at all about readability, just about find a path - any path - to something that works. As soon as I have that readability starts to matter and the first order of battle is then to refactor out all the dead ends and to make the whole thing look good. That's because the project now has long term perspective.
And the worst thing is using languages/libraries/frameworks that presume everyone needs engineering when you need to wizard.

There's two many people that have swallowed SOLID whole and can no longer see good engineering as a trade-off against other factors.

For example, being strict about having the smallest possible public API and making most methods private protects me from future breakage that might never be an issue (I might never upgrade) but forces me copy/paste vast globs of your code into my own if I need access to something you didn't anticipate. (and that's assuming I have access to your source. Worst case is that I have to reimplement things that already exist in the code I'm interfacing with)

Python got this right. Private methods are a weak or strong hint that you might want to think twice before calling them. But you're the boss at the end of the day.

>And the worst thing is using languages/libraries/frameworks that presume everyone needs engineering when you need to wizard.

I think this is why it's easy to point a thousand things built in python which people use every day (like instagram), while in, say, haskell, there are barely a handful (pandoc, facebook spam filter, etc.).

This is similar to Martin Fowler’s Design-Stamina Hypothesis [0].

[0] https://martinfowler.com/bliki/DesignStaminaHypothesis.html

I like the way you characterize this but what on your thoughts on why you can't be a wizard with a typed language? Seems to me that if you start with something like Go or Typescript you cover a decent middle ground of foregoing a lot of boilerplate while having code that you don't need to be a wizard to understand.
> You're just a bunch of engineers who know the whole codebase inside out, you're pretty certain of what you're doing. In short, you're wizarding. If you grow big enough, this approach slows you down greatly, and you need to switch to engineering.

I've never heard of this before... I love it. Thanks for bringing this up.

> When you're a small startup, what matters is the expressiveness of your language, and the ability do do a lot of things very very quickly. Type safety, performance, readability, those things don't matter. You're just a bunch of engineers who know the whole codebase inside out, you're pretty certain of what you're doing.

I'm not familiar with this use of the term "expressiveness".

My understanding is that expressiveness (as per "On the expressive power of programming languages", Felleisen 1991 [0]) has to do with capabilities that a language has that separate it from another language. C is more expressive than Python in that it gives you direct access to memory management, whereas Python is more expressive than C in that it provides inheritance/OO. (These are just examples.)

Type safety, performance, and readability are all wholly separate from expressiveness, I think. A language's type system and performance benchmarks have nothing to do with the expressive power of a language outright, and "readability" is entirely subjective to begin with.

So: would you mind elaborating on what you mean, exactly, by "expressiveness of [a] language" here?

---

In fact, most of what you (and the linked article) are talking about has to do with the dynamic/static spectrum, not this "wizarding/engineering" spectrum you've coined (though I do kind of like the idea of that for discussing development methodologies).

The article is all about how the dynamically-typed nature of Python allowed for rapid iteration at the beginning of the Instagram project, but has since hindered further progress as they've grown larger. But now they feel they can't just rewrite it all in a statically-typed language because of the engineering overhead involved.

On this note, I want to go to your last point:

> I wish there was a language that let you move gradually from one end to the other, exactly when you need to.

With regard to the dynamic/static distinction, there are languages that allow you to move "gradually from one end to the other", and they are (aptly) called gradually-typed languages.

Gradual typing was invented by Jeremy Siek and his PhD student, Walid Taha, back in the mid-2000s at Indiana [1]. In this discipline, you can have a statically-typed codebase with local dynamically-typed regions. You get all of the static guarantees for everywhere that they can be made, and dynamic regions impose runtime checks to ensure consistency. (This connects closely to contracts, which are primarily worked on by Robby Findler at Northwestern, I think.)

Unfortunately (to me), it seems like a lot of these languages are implemented in terms of existing dynamically-typed languages. For example, Sam Tobin-Hochstadt (Indiana) created Typed Racket, which is (of course) built upon Racket but provides a gradual typing discipline. Wherever possible, static types are checked, and everywhere else utilizes contracts to guarantee runtime consistency.

Anyway, all this is to say: the technology exists, technically, but is in its infancy. There's no doubt it'll be some time before it sees widespread use throughout industry. Sam wrote up a brief overview for the SIGPLAN Perspectives blog recently, if you're interested [2].

[0] https://www.sciencedirect.com/science/article/pii/0167642391...

[1] https://wphomes.soic.indiana.edu/jsiek/what-is-gradual-typin...

[2] https://blog.sigplan.org/2019/07/12/gradual-typing-theory-pr...

I expect in this context, expressiveness means something like "the ability to describe the relevant stuff in the code with minimal noise", which might map to having good abstraction.

I find that pythons OOP + functional aspects, combined with a good understanding of the language hits a sweet spot here. One that simply can't be reached in c/cpp/go/java/haskell, and which is much easier to reach than in js/rust/other langs where I think it is possible.

My definition of expressiveness is basically similar to what Paul Graham (@pg) says. He also calls this "powerfulness" in his famous "Beating the averages" essay[1]. In short, a more expressive language is a language that lets you express more with less. Rails, with its "has_many :books" is very expressive, Assembly is the other end.

The wizarding/engineering spectrum was coined by the article I've linked to[2]. I think the post is exactly about that, first Instagram was wizarding and they had a suitable language for wizarding, now they're engineering, but their language is still only good for wizarding.

As I've said in a sister comment, it's not just about static typing, but metaprogramming/macros/side effects everywhere etc. There's more to the expressiveness/powerfulness than just types. While gradual typing is certainly an improvement, I think we need more research in this direction.

[1] http://www.paulgraham.com/avg.html

Who is starting large-scale new projects in Java in 2019?
Countless companies, huge and small -- from Apple and Amazon, to Google and your friendly local startup, plus all the enterprise world that's not a .NET shop...

In what parallel universe is not Java immensely popular or not used for green projects?

Hopefully in every universe where Kotlin exists.
I can think of at least one universe where Kotlin exists, but most new JVM development is still in Java.
Kotlin is meaningless without Java.

It is a fools errand to think Kotlin/Native would ever overtake Java.

Only on Android it might have a future, if Fuchsia never becomes a thing.

Kotlin might become the default Java syntax, that's what the GP is saying. And I agree.
Java is Java.

On what concerns the JVM, Kotlin hype will be over in a couple of years, and it will get as much use as Scala, Clojure, Beanshell, Groovy enjoy nowadays.

Guest languages never get to own a platform, and with time all platform languages end up getting enough features that the large majority of developers never bother with extra tooling, debugging layer and idiomatic wrapper libraries of the guest languages.

Kotlin != Java .... unfortunately. Sorry Jetbrains.
Can't say about new ones, but after a couple of years of working on a huge Python project, I would accept rewriting it in Java without a whim. I have equal experience in Python and Java by now. Funny thing, I returned from my vacation a few months ago, and started writing my first code after it. Of course, I was more concerned with thinking what am I writing instead of how. Then I looked up and noticed I started typing Java instead of Python. And that's three years after the last time I've written some Java code.
Oh, just Apple, Amazon, Google, Netflix and likely every hospital, utility company, police force, military, or bank you depend on. And the people programming the robot swarms that pack your groceries (https://www.infoq.com/presentations/java-robot-swarms/).
For large scale projects, Java and C++ remain the go-to languages. I've seen a little bit of Go start to show up but no others. Other languages are used for libraries (Rust, C), only at certain employers (OCaml, Erlang), or for small-scale projects (nearly everything else).
On C++, inertia is a wonderful thing. Java has the benefits of extensive dependency injection and JVM/ecosystem tools that lower the risk of deployment of code. .NET also provides the controlled "managed code" environment of CLR.

Why any enterprise would use C++ for standard "business" or "large scale" programming makes no sense to me.

Enterprises want stability, not speed to market. Most of their infrastructure changes slowly (as in features deployed once or twice a year maybe). They have stable support mechanisms for this, including long and complex processes of approval.

As ex-C++ dev, that lives in the Java/.NET worlds since 2005, because they still don't cover all use cases where C++ might be needed.

So while you might not write it as full stack C++, a couple of native libraries might be required as dependency, to access OS features, give some help to the AOT/JIT compilers, or in Java's case implementation of more machine friendly data structures.

One of my clients handles 90% of all the pbm routing in the US, which is millions of transactions per second. They started to completely modernize the application on java... primarily Spring Boot and Apache Geode. After some optimizations they are very happy with the performance and expressiveness of modern java.
I said new projects. Not upgrades.
Not the OP, but I understood their comment to mean 'a rewrite in Java of a legacy system written in some other language'?
Exactly right. Java has lots to improve on but there seems to be unsubstantiated hate towards Java on HN which I find contrary to what happens in the real world. In the real world companies find hiring Java programmers relatively easy (maybe not the best and brightest) who can get a project off the ground easily
A big chuck of Netflix is written in Java, and they make new stuff in Java all the time.
I write JavaScript all day and wish I could write in Java.
Google
It's a good question. Why would you pick Java over Go or C++?
C++ is mostly in a different but overlapping use case these days, used for system software or where performance is critical. Go is still a niche language most developers haven't heard of and with no readily available for hire talent pool.

I'm not a fan of the language, but java has a huge number of developers, a rich mature ecosystem of software and is quite productive (enterprise patterns aside), it's a good sweet spot for most companies.

Why would you pick Go or C++ over Java?

C++ is a hydra of complexity, sure it has it's place, but it's not nearly as productive as Java for your typical web application.

Go is almost the opposite, so simple it lacks features like generics. The last time I used Go it had fundamental usability issues around dependency management(although I think recent versions have improved on vendoring a little).

> C++ is a hydra of complexity, sure it has it's place, but it's not nearly as productive as Java for your typical web application.

Modern C++ well is as productive ( probably even more productive ) as Java. The main issue with C++ is recruitment, C++ engineers are rare because C++ is barely teached.

C++ is barely taught in ProgrammerGenerationFactories because "modern" C++ still allows "old" C++ and makes it difficult to stop developers from doing that.

Just like MISRA tries to constrain C programmers from doing dumb things in the embedded world, "modern" C++ tries to the same in the business world. But there isn't an easy way to enforce it, especially when you're outsourcing to some code sweatshop.

> But there isn't an easy way to enforce it

Every mature enough language has a subset that you need to avoid. Including Java. This is precisely due to this kind of things that every company need to have coding guidelines and proper static analysis tools.

> especially when you're outsourcing to some code sweatshop.

If you outsource your dev to cheap, other side of the world, low quality engineers. Then you deserve your problem, in any language.

I worked in the past for a company (embedded programming) that had an entire team of expensive engineers in Luxembourg just to fix the stupidities of an other team of outsourced engineers in India.

Any good university has C++ related classes.

The problem with modern C++ is having many devs to actually make use of it.

Many devs don't care to follow up on modern language X best practices, rather just typing away something that works.

Plenty of companies free of magpie developers.
I asked for examples, not platitudes.
And yet you only offered a misinformed platitude in the form of a question.

Google does all kind of new Java work (Golang contrary to myth, is just one of the languages Google uses for internal stuff, and niche at that), Amazon of course, most of Apple's backend services are Java, Twitter, AirBnB, Uber, LinkedIn, TripAdvisor, and tons of others use Java, and write green stuff in it all the time...

Uber back end is mostly Go.
Twitter, and they write some really good open source stuff too. If you're writing rpc services in java I'd almost argue you should default to considering Finagle:

https://twitter.github.io/finagle/

Pick random names out of Fortune 500 companies.

Java projects get done all the time.

Additionally, even though it isn't standard Java, I have plenty of new apps running on my pocket.

Plenty of teams within Amazon
What you call Wizarding I call "ordinary Software Development". A software developer spends ~70% of their time writing features and the rest mixed between organization/planning/roadmapping etc. A software engineer spends ~30% of their time writing features and the rest of it managing technical debt and making long-term investments towards better features and processes.

Too many companies need devs but have engineers, or they need engineers but only have devs :/

I’ve never known anyone to distinguish between the two roles, and they seem to be used interchangeably in the industry.

(Except those people who claim software engineers aren’t real engineers)