HNHacker News
TopNewBestAskShowJobs

jackcviers3

437 karma · joined February 29, 2012

submissionscomments
jackcviers3··on Don't start with microservices – monoliths are your friend
On the TDD argument, you should know what a return for a function f should be when given an argument b. Ideally, you have a set of arguments B to which b belongs, and a set of results C to which the return belongs. Your tests codifies mapping examples from B to C so that you can discover an f that produces the mapping. Take another step and generate random valid inputs and you can have property-based testing. Add a sufficient type system, and a lot of your properties can be covered by the type system itself, and input generators can be derived from the types automatically. So your tests can be very strong evidence, but not proof, that your function under test works as expected. There's always a risk that you modeled the input or output incorrectly, especially if you are not the intended user of the function. But that's why you need user validation prior to final deployment to production.

Likewise, with a microservice architecture, you have requirements that define a set of data C that must be available for an application that provides get/post/put/delete/events in a set B to your service over a transport protocol. You need to provide access to this data via the same transport protocol, transform the input protocol to a specified output protocol.

You also have operational concerns, like logging that takes messages in a set C and stores them. And monitoring, and authorization, etc. These are present in every request/response cycle.

So, you now split the application into request/response services across the routing boundary-> 1 route = 1 backing model. That service can call other apis as needed. And that's it. It's not hard. It's not even module-level split depolyment. It's function-level deployment in most serverless architectures that is recommended because it offers the most isolation, while combining services makes deployment easier, that's mostly a case of splitting deployment across several deployment templates that are all alike and can be managed as sets by deployment technologies like Cloudformation and Terraform [1].

You can also think of boundaries like this: services in any SOA are just like software modules in any program - they should obey open/closed and have strong cohesion [2] to belong in a singular deployment service.

Then you measure and monitor. If two services always scale together, and mutually call each other, it's likely that they are actually one module and you won't effect cohesion by deploying them as a single service to replace the two existing ones. easing ops overhead.

Not deploying and running as a monolith doesn't mean not putting the code to be run into the same scm/multiproject build as a monorepo for easy shared cross-service message schema refactoring, dependency management, and version ingredient. That comes with its own set of problems -- service projects within the repo that do not change or use new portions of the comm message message schema shouldn't redeploy with new shared artifact dependencies; basically everything should still deploy incrementally and independently, scaling it is hard (see Alphabet/Google's or Twitter's monorepo management practices, for example); but there seems to be an extra scale rank beyond Enterprise size that applies to, it's very unlikely you are in that category, and if you are you'll know it immediately.

We like to market microservice architecture as an engineering concern. But it's really about ops costs in the end. Lambdas for services that aren't constantly active tend to cost less than containers/vps/compute instances.

1: http://serverless.com//blog/serverless-architecture-code-pat...

2: http://www.cs.sjsu.edu/faculty/pearce/modules/lectures/ood/m...

jackcviers3··on On “Trojan Source” Attacks
Way to blame a bug in your editor on someone else's.

Emacs autoconverts between the two line ending styles at open and close, unless you expressly tell it to do otherwise [1]. You had malicious editors using emacs. The editor wasn't the problem, the people using it were and they had to go out of their way to manage it.

1. https://www.emacswiki.org/emacs/EndOfLineTips#:~:text=(The%2....

jackcviers3··on Scientists have found a way to harden wood to make a knife that rivals steel
I've accidentally gotten multi-tools through the body scanner before. I just forgot I had it in my pocket.
jackcviers3··on Scientists have found a way to harden wood to make a knife that rivals steel
So you can do this at home with a hydraulic press and a torch then? Would be kind of fun.
jackcviers3··on Ask HN: I'm making like 0 friends at college, how to fix it?
Go out and drink beer on a Wednesday night. Go to football games. Go to dance clubs. Go to music venues. Go to parties.

Then talk to people. That is literally all it takes in college.

If you stay in your dorm room night after night, especially in your freshman year, you are going to have a hard time connecting with people. You need to get out more.

1 night during the week, 1 night on the weekend are for meeting people.

You can study / homework / club stuff the rest of the time.

jackcviers3··on Magit, the magical Git interface (2017)
Yeah, it's definitely the mnemonic single-key and terminal gui that makes magit great. That, and the genuinely low occurrence of bugs in the software.
jackcviers3··on Was Google Earth Stolen?
It's not that - it's the inertial glide to a stop that makes it feel like a big piece of paper that was innovative at the time. See my other comment.
jackcviers3··on Was Google Earth Stolen?
An invention is the embodiment of an idea. It's not enough to think of a humanoid robot. You have to think of and implement the idea.

Your interface is just a fantasy. You haven't taken any steps towards making it a reality. There's no demonstrable prototype. There's no documented research into it's principles that you have uniquely contributed directly to.

That's the difference. You haven't undergone any inventive steps, because you haven't created anything. You have a vague idea that such a thing may be possible, but haven't undergone the rigor of understanding and implementing a basic example of what you have conceptualized.

This isn't to be confused with comercializing the idea, figuring out how to market and produce it to meet demand, package it, and deliver it. That's a wholly different process, parts of which itself may require further inventions.

jackcviers3··on Was Google Earth Stolen?
I'm speaking of the inertial scrolling tween that causes the coast to stop pre-Google Maps era, and makes the interface feel like handling an actual physical object, not the sliding of things into a viewport. Viewports and tiling had been done long before that time period.

That physics combination with drag and drop interaction was not obvious in the time period we are discussing. The closest thing I can remember to that sort of thing were side scrolling games that accelerated with the character centered on the screen according to the game's physics, and possibly paddle-based game physics dating to the Atari era.

Slowly increasing scroll was present in some interfaces. But not tweened stop.

My main point is that clever people often dismiss what they find trivial to be obvious to others, and while that's often the case once many examples have been observed, it isn't prior to the exposure to many examples.

jackcviers3··on Was Google Earth Stolen?
It's not obvious. It's just smart. And decades of momentum based apis have caused you to think of something you'd probably never actually seen in a ui at the time as non-novel.

Inventions are often that way -- think of the mousetrap. People knew springs pinched the crap out of you when released. But nobody had made a device to kill mice, yet untilthat was invented.

I don't know what the cognitive bias for thinking that because you thought of an idea it must be obvious is, but it exists. I say that as a patent inventor.

jackcviers3··on Unplanned Freefall? Some Survival Tips (2001)
Reel Physics concluded from frame analysis that is possible [1].

https://youtu.be/foUn_6W9N-Y

jackcviers3··on Docker Desktop no longer free for large companies
Before Docker Desktop there existed a solution called docker toolkit that worked exactly like this. The only problem is that occasionally internal corporate networks will use the same ip address and you have to customize that by building your own docker engine.
jackcviers3··on Git password authentication is shutting down
I weep for the world now that git is too difficult to understand.

It used to explain itself through building your own version of git. You have a file named hash that tracks changes on each file named foo... now all the changes are in a directory named hash...now your changes have to be merged with others...

It's kind of up there with next steps after if then else fi. Some things will always be difficult, but diffs and commit and picking one of two possible diffs from two windows should not be one of them.

jackcviers3··on The Framework Laptop is now shipping
1 watt active is low. But so is 2 for dimm ddr3 [1]. They switched to make upgrading more difficult.

"Typical Power Consumption of PC Components - Power Draw in Watts" https://www.buildcomputers.net/power-consumption-of-pc-compo...

jackcviers3··on The “bugs are 100x more expensive to fix in production” study might not exist
I bet it was true, in the context of the time:

Card punches, shared compute time, physical packaging and shipments; imagine the cost of fixing a bug in a program in the rope ROM of the Apollo guidance computers (had they actually bothered fixing one).

I'd love to see the study redone in our postmodern OTA, client/server, no physical media world. The physical overhead cost of fixing a product bug would likely be orders of magnitude lower than in 76, but the units deployed in the field would be many many orders of magnitude higher; so cost per unit is down, but there are waaaaay more units in production. If the bug caused a material cost to the user, I could easily see it being very expensive.

As for there being no experiments; computer science is more of a mathematical discipline than a scientific one. You might say the same thing about theoretical physics, but I still think it's likely that we can't break the speed of light.

At any rate, the IBM study may not have existed, but others that took me less than ten minutes of googling to find definitely did, and do show results in the same order of magnitude as the questionable IBM study.

The question I have is - what are we really arguing for here by refuting this data, allowing more bugs to reach production? Having more broken code in the wild than we already do? Less rigorous development methods? There's barely any rigor in SWE as it exists today, anyway, yet things mostly work through a generous application of trial and error.

jackcviers3··on The “bugs are 100x more expensive to fix in production” study might not exist
I thought the numbers came from:

Boehm, B.W. 1976. “Software Engineering.” IEEE Transactions on Computer SE-1(4):1226-1241.

As cited in "The Economic Impacts of Inadequate Infrastructure for Software Testing" by RTI.

https://www.nist.gov/document/report02-3pdf

Pretty sure that both of those papers exist, and the NIST table cites up to 990x and refers to the 76 Boehm paper, not any IBM org.

jackcviers3··on Thread Pools on the JVM
Author mentions scala. Both ZIO[1] and Cats-Effect[2] provide fibers (coroutines) over these specific threadpool designs today, without the need for Project Loom, and give the user the capability of selecting the pool type to use without explicit reference. They are unusable from Java, sadly, as the schedulers and ExecutionContexts and runtime are implicitly provided in sealed companion objects and are therefore private and inaccessible to Java code, even when compiling with ScalaThenJava. Basically, you cannot run an IO from Java code.

You can expose a method on the scala side to enter the IO world that will take your arguments and run them in the IO environment, returning a result to you, or notifying some Java class using Observer/Observable. This can, of course take Java lambdas and datatypes, thus keeping your business code in Java should you so desire. It's clunky, though, and I wish Java had easy IO primitives like Scala.

1. https://github.com/zio/zio

2. https://typelevel.org/cats-effect/versions

jackcviers3··on Automating programming: AI is transforming the coding of computer programs
Incorrect. Recognition applications have been able to track objects across disparate frames of video even when obscures from camera view, for at least a decade now [1].

1. https://youtu.be/1GhNXHCQGsM

jackcviers3··on Automating programming: AI is transforming the coding of computer programs
And what makes the voice in your head can't understand what it says.

There are two parts, at least, to sentience: the observer and the actor. In yogic traditions, "you" are the part that "hears" the voice in your head, not the voice itself. I think this, while it may not necessarily be true of "real" minds, has a lot of utility when it comes to designing artificial ones.

I think general AI is probably going to be lots of individual parts structured around a central observational unit, which then makes preprogrammed higher level decisions on where to distribute inputs and outputs of several external nn, score the results of it's actions, and then use the results for reinforcement and/NEAT or something similar to improve it's central decision making.

This technique of a simple, preprogrammed decision maker given inputs and outputs of stochastic processes is already in use in autonomous navigation (see the GT robotics course on coursera)[1].

The actor doesn't need to understand meaning, the observer assigns meaning to the actions of the actor.

1. "Free Online Course: Control of Mobile Robots from Coursera | Class Central" https://www.classcentral.com/course/conrob-404

jackcviers3··on Compiling Rust is NP-hard
I get this question about large scala codebases too - clean build and test cycles for things on underpowered containers in cloud build pipelines are in the tens of minutes sometimes.

One: my local dev machine has 16 gigs of memory and 16 cores. What takes the tiny docker container with 1 gig and 2 vcpus 30 minutes takes my computer about 1.

Two: Incremental compilation and testOnly make build/test cycles maybe a second / twenty seconds max, and most of that is test runtime on complex property based tests.

You just get by without a clean compile most of the time after you build the project the first time. And really, a lot of builds spend an inordinate amount of time just pulling in external dependencies (which are also cached on my local machine, but not on the containerized builds a lot of the time).

jackcviers3··on How to Migrate a Git Repository from GitHub to Bitbucket
It was cheaper for private repositories (free for teams less than 5) for a long time. I think Gitlab is cheaper now.
jackcviers3··on How to Migrate a Git Repository from GitHub to Bitbucket
Huh. Been using it for the last 5 years with pipelines over the last year and had no issues. The pipelines do require weird strategies for big scala compiles though.
jackcviers3··on Stop mocking your system
Mocks and fakes are used to test your code under isolation - like an experiment, only one variable should be under test at one time.

In addition, most stateful code that can fail, does so intermittently, is susceptible to load delay at scale that can effect your customers and your wallet, may not be at the released version you expect, requires clean-up so that your storage isn't clogged with useless test data, which may or my not be mocked, effects downstream analytic services, etc.

Isolate your unit tests with fakes and in-memory test-local storage, run proprety-based tests with generated data for both inputs and returns that confor to the constraints of your domain, including possible expected error conditions, and unit test in parallel. If you do these things, you can and will have many of the benefits of integration tests, and won't waste time writing carefully crafted test data/scenarios.

Integration test when you get a bug in production, and run the integration tests in an isolated environment identical to the one you deploy to prior to deployment to a place where your users will be interacting with your code. Cleanup can then simply be destruction of the test environment. There was a talk on blue/green deployment the way AWS SRE does it a few years back that was great. While I have the notes, I can't find the talk, but it's a much more complex process than running tests against your up system.

jackcviers3··on Newspeak and Domain Modeling
If you restrict yourself using scalafix [1](a linter), scalazzi [2](a linter) or wartremover [3](another linter), you can prevent your code from returning `Any`, `AnyRef`, `Nothing`, or `Unit` and prevent yourself from using `Throw` , partial std lib functions, or `NonUnitStatements` without explicit annotation.

This effectively locks you into writing pure code (you can extend the linter to cover other things like not using `Future` or not using Java libs outside of `MonadError` from cats[4]). The linters operate on typed ASTs at compile time, and have plugins for the most popular scala build tools. Coupled with `-XFatalWarnings', you can guarantee that nothing unexpected happens unless you explicitly pop the escape hatch, for the most part.

You can still bring in external libraries that haven't been compiled with these safties in place, so you aren't completely safe, but if you use ZIO[5]/Typelevel[6] libraries you can be reasonably assured of referentially transparent code in practice.

There are three schools of thought, roughly, in the scala community towards the depth of using the type system and linters to provide guarantees and capabilities, currently:

1) Don't attempt to do this, it makes the barrier to entry to high for Scala juniors. I don't understand this argument - you want to allow runtime footguns you could easily prevent at compile time because the verifiable techniques take time to learn? Why did you even choose to use a typesafe language and pay the compilation time penalty that comes with it?

2) Abstract everything to the smallest possible dependency interface, including effects (code to an effect runtime, F[_] that implements the methods your code needs to run - if you handle errors, F implements MonadError, if you output do concurrent things, F implements Concurrent, etc.) and you extend the effect with your own services using tagless final or free.

3) You still use effect wrappers, but you bind the whole project always to use a concrete effect type, avoiding event abstraction, thus making it easier to code, and limiting footguns to a very particular subset (mainly threadpool providers and unsafeRun or equivalent being called eagerly in the internals of applications).

My opinion is that smallest interface with effect guarantees (#2) is best for very large, long maintenance window apps where thechoice of effect runtime might change(app), or is out of the devs' control (lib); and #3 is best for small apps.

TL/DR; You can go a really, really long way to guaranteeing effects don't run in user code in scala. Not all the way like Haskell, but far enough that it's painful to code without conforming to referential transparency.

1. https://github.com/scalacenter/scalafix

2. https://github.com/scalaz/scalazzi

3. http://www.wartremover.org/

4. https://typelevel.org/cats/api/cats/MonadError.html

5. https://zio.dev/

6. https://typelevel.org/

jackcviers3··on Unison Programming Language
Semver doesn't help in the case of transitive binary incompatibility. If lib A depends on B v2, and lib C depends on B v1, and application D depends on A and C, you cannot load a version of B that satisfies D, A, and C. Semver tells you that B 1 and B2 are incompatible, but not how to solve the issue.

Unison solves the issue - there isn't any binary incompatibility, because the transitive versions of Bv1 and Bv2 cannot be in conflict - the function references are to guaranteed unique and different versions of the art.

As for bug fixes - you can specify in your code exactly which version to use.

As for editors needing to be unison aware - they just delegate everything to the compiler via lsp and bsp.

Bug fixes are no more difficult than making the change. A new version is created, and your code can now depend on it. Old code will still run off of the old version. It's up to the code owner to decide to use the new, but fixed version.

Version control is all handled in the language itself.

As for the hard hashing problem... Runar is a particularly intelligent individual. I expect that his algorithm works pretty well.

The first argument about storing the ast is moot in an age where cached compiled typescript, Python, and .class files take up inordinate amounts of disk space.

> Solutions that dynamically discover code dependencies and automatically run tests exist for both procedural and functional languages

Eh. Piping and yarn ain't got nothing on maven and ivy and apt. But yes, dependency management isn't anything new under the sun. Dynamically resolving individual function versions in packages alongside binary incompatible functions is.

jackcviers3··on I spent 5 years writing my own operating system
Woz, meet Steve.
jackcviers3··on How to Become a Bad Developer
To be fair, bugs are highly correlated with line count, and all code is inscrutable in the absence of knowledge.

I do like vocabulary-based metrics for legibility/difficulty; Halstead metrics can't tell you how good a piece of code is, but they can tell you how difficult it is for others to read, empirically.

The old 7 +- 2 rule applies, if you are using more than 9 functions/methods and arguments, the vocabulary of a function is difficult.

f compose g is 0.5. It has 3 elements. It's not hard to understand.

def compose (f: B=> C, g: A => B): A => C = x => f(g(x))

Is 2.5, counting all the definition parts and type level stuff. It has 14 distinct elements. If you don't know the pieces, it's really difficult to read if you don't know any of the parts. If your language doesn't care about types,

def compose(f, g) = (x) => f(g(x))

Comes to 2.1 by my count, but you have to look up fewer things at 7, so it's easy to read for new people.

Now, the types make it easier once you advance beyond a pure beginner- the constrain the function to only one possible implementation. But if you're totally unfamiliar with things, they make stuff unreadable. Even in tiny functions.

jackcviers3··on Don't Feed the Thought Leaders
KISS -

Most people would not recognize simple code if it hit them I'm the mouth.

KIES (keep it easy, stupid) is what they mean, and that means following idioms, frameworks, that are familiar.

jackcviers3··on Teach Yourself Programming in Ten Years (1998)
I hate to do this, but:

Stop equating programming and bus drivers or waiters. They aren't equal. Programming is difficult in its depth and breadth in a way that waiting tables is not.

Stop treating all programming tasks as equal. They are not. Most of the easy ones are only easy because they run on top of all the difficult programs. That is the goal of the difficult programs, to make it possible for laymen to do work that would require an expert for certain repetitive tasks if the underlying program did not exist.

Most people would not be capable of getting Hello World to run without the OS and high-level abstractions that take expertise to develop. And making that possible requires many many experts in many different kinds of programming.

Stepping off of the single thread and single machine model into concurrent and distributed computing to enable even more laymen to make stuff takes even more experts.

There is a meme in the industry that people shouldn't strive for expertise. We still need more experts, and you become an expert through practice while still a layman. Nothing would be worse off if we had more experts capable of deciding when to use something extensible off of the shelf to enable laymen to do simple tasks, and when to build a platform to eventually enable that task to be done by a layman.

A lot of things would be worse off if we encouraged all people to stop at the layman level. We should encourage everyone to at least become a layman for their own enrichment. But we should also encourage laymen to strive for expertise to lower the barriers to entry to produce more laymen capable of doing simple tasks.

jackcviers3··on Functional Programming in Go with Generics
Exactly. Is it RT? Is it well-typed? Is it as simple as it could be? Is there a small number of inhabitants? Ship it. Using some weird recursion scheme when fold right will do?
← PreviousPage 5 of 9Next →