So, please consider the difference between complex and unfamiliar.
2,861 karma · joined January 27, 2011
So, please consider the difference between complex and unfamiliar.
A common use for closures in imperative languages is to have custom control structures similar to `if`, `for`, `while`, etc. And in imperative languages mutating values is very important, if you want a new control structure you have to be able to mutate your surrounding variables.
Smalltalk only uses this kind of control structures based on closures. Ruby also uses this a lot, it even changes the meaning of `return` in closures (blocks) to return not from the closure but the enclosing function.
This says a lot. I'm sure Microsoft has very smart and competent engineers, yet they still get all these bugs. It's because manual memory management is really, really hard.
I'm baffled to keep hearing some people saying that manual memory management is okay when the evidence is this huge.
Perhaps for very simple programs is okay to do manual memory management, but for complex ones like most of them nowadays, it seems close impossible for humans to write safe programs.
If you think about it, a package manager workflow (like maven, nuget, etc) is also a form of incremental compilation. You compile your libraries only once and publish them in binary form in some sort of repository and then import them. The difference is that if you want to make a change in both a library and an application this becomes hard to do with packages, while very easy with a monorepo and Bazel.
A monorepo oriented build tool such as Bazel will make sure that you don't compile more than needed. This is extremely efficient. In fact, in my company with a few hundred projects we tried it and it was 10 times faster than our previous Maven based workflow. These tools also help you with dependency management so you keep your code organized. The killer feature is that you finally have true Continuous Integration because the build tool will figure out any dependencies of the code that you change, rebuilding it and testing to make sure that nothing is broken.
We all have different skills, we can be good at programming or at graphic design or at music composition or at communication. Having these skills give you advantage, but you have to remember that at some point you didn't have those skill, those were cultivated. If you treat finishing things as a skill that has to be cultivated, where you suck at first, but with enough practice you get better and better, then you get into a mindset that will allow you to be more and more disciplined. So start small, take smaller projects, as small as possible. Make sure you finish them, slowly increase the difficulty level, you will get there.
[1] https://makegames.tumblr.com/post/1136623767/finishing-a-gam...
An example to illustrate this: You have a library X that depends on Library Y and application A that depends on X. Then when you make a change in Lib Y you want to make sure that the change doesn't break X or A. If A, X and Y live all in separate repositories with separate CI pipelines, then this is hard/cumbersome to solve. In a Mono-repo, if you use a build system like Bazel, it's very easy.
I believe when you say that your Jenkins setup is a mess, we experience a similar situation with GitLab CI. But I think it's important to realize that CI stands for "Continuous Integration". And running pipelines for every individual repository is not really testing integration, it's just testing an individual component. The solution is to use Bazel or similar. Then you will have proper CI.
Developers who believe specifying types doesn't have any drawbacks are still early in their careers :D
Wow! No!
If this is what you feel, you might have been doing unit testing wrong the whole time! Unit testing is a powerful technique that is useful regardless of your language. the first unit testing libraries and frameworks started with Statically Typed languages.
That said, I think FP still has one disadvantage which is performance. This is because the underlying computer is 100% imperative and there are still a lot of work to make FP patterns efficient.
FP works well for interactive UIs because the number of objects that change in GUI is very small, so performance is not an issue. But take a video game, for example, then you have thousands of moving objects that are updated at 60FPS. Once you get that you notice is really hard to write the logic as a set of immutable state changes, object allocation becomes a bottleneck and then you discover that OO is more suitable, at least for now. Maybe later there will be ways to efficiently translate the FP patterns into the imperative CPU models.
Of course the Monorepo is not free of downsides, those mentioned in the original article are real, although a bit exaggerated in my opinion. VCS operations can be slow and scaling a VCS system is challenging, but possible. And the risk of high coupling and a tangled architecture is also very real if you don't use a dependency management system like Bazel/Buck/Pants.
But in my opinion the downsides of the Polyrepo are much worse and much much harder to fix. The main problem is that you need a parallel version control system like SemVer on top of your VCS. SemVer is fine for open source projects but for a dynamic organization is a nightmare because it is a manual process prone to failure. SemVer dependency hell is really hard to deal with and creates a lot of technical debt.
Additionally, once you go Polyrepo you lose true CI/CD. Yes, you still have CI/CD pipelines but those apply only to a fraction of the code. Once you get used to run `bazel test` and you know you will run every single test of any piece of code that could depend on the code you just changed, you never want to go back. Yes, you could have true CI/CD with Polyrepos, but it requires a lot of work and writing a lot of tooling that does not exist in the wild. It is cheaper to invest in scaling your VCS in a multi-repo.
HN is a simple website. I'm glad that they decided to keep the code simple as well. I love the fact that the code almost fits in a single screen. Zero dependencies. You can understand the entire code in a few minutes.
I think new developers greatly underestimate the negative impact of dependencies. Today, something like create-react-app downloads hundreds of megabytes of dependencies. Libraries, transpilers, compiler extensions, and various tooling. Of course all those things come with many advantages, but what most people miss is the huge cost of all that. The increased complexity introduced by dependencies. The unnecessary bloat.
Sure, the way you name your variables have an impact on readability. But it's nothing compared to adding thousands of dependencies to your project.
Python is maintained by volunteers. Providing support for more than 10 years is more than enough in my opinion. The community has moved on to 3, but I'm sure there will be companies doing consulting and fixing 2.x bugs for a fee.
How is it possible that Doom was developed on NeXT?
As far as I know, Doom was first released for MS-DOS. Wouldn't be really hard to develop a MS-DOS game from another operative system and another CPU architecture? Specially at the time without engines abstracting platform details?
I recommend this presentation by Victor Stinner at FOSDEM 2018 which talks about it: https://fosdem.org/2018/schedule/event/python3/
Some particular names used in the examples were introduced by Python. For example the `enumerate` function or `zip`. I haven't seen those names used in programming languages older than Python, I know I might wrong. The point is that it's not crazy to believe that Rust design does borrow some things from Python (among other languages of course).
The article is comparing some particular Python idioms with Rust. The fact that those are functional I think is not particularly relevant. It's like someone seeing a comparison of Java and Go and saying that those are just the principles of imperative programming, or object oriented programming.
So instead of typing:
AbstractConcreteFactoryFactory factory = new AbstractConcreteFactoryFactory();
You can just type it only once: var factory = new AbstractConcreteFactoryFactory();I like Java. It's simple and performant and has excellent tooling. I just don't like that sometimes I see a lot of incidental complexity in its ecosystem.
Hibernate does not save you from writing queries. You are still writing queries, just in a language different than SQL (e.g. JPA). It's an abstraction layer. The problem is that this abstraction is very leaky, so if you really want to write performant code with Hibernate you do need to understand how SQL and your database works. And if you really understand how it works, you end up realizing that the abstraction is kinda pointless because SQL is already a really fine abstraction over your database.
And if you need to scale, for example working with a replication setup with multiple db servers and having to deal with eventual consistency, then Hibernate really complicates things.
I think Hibernate is a good example of something that makes things easier at the beginning. At the cost of enormous complexity and difficulty in the long term.
This huge call stack has been designed to make your life as a developer easy but the price you pay is an enormous amount of complexity.
I've been working a lot with a similar Java web stack and I feel how painful this complexity is. What is worse, is that I think that a lot of this complexity is incidental. There are libraries and frameworks designed to make some things easier, but in the process end up creating a lot of problems that then requires another library or framework to overcome that problem which also has other problems and so on... The result is a huge stack like this.
One concrete example of this is Hibernate. A tool designed to make it easier (apparently) to work with databases, but in the end create so many problems that the medicine ends up being much worse than the disease.
Resolving an HTTP request that returns a the result of a database call should not be this complicated! HTTP is simple! Why do we need so many calls to so many things. I'm not advocating for a flat stack of course, but certainly a stack this deep is a clear sign that something is wrong.
I very much agree with Rich Hickey, we need to stop thinking about how to make things easier and start thinking how to make them simpler.
I think it's unlikely that the community will bring support. Most of the community already jumped to 3.x, and those who haven't will do in the next two years. Also, supporting legacy code is not something that volunteers love to do.
I agree that manually building SQL strings can be cumbersome. But in practice you can use libraries that allow you to easily create them programmatically.
The JSON queries of MongoDB on the other hand, while they are simple to use programmatically, they are a pain to use outside of your program, for example for running ad-hoc queries, or generating a report from your data. And there is no library to fix this.