oppositelock
Joined 13 years ago
Joined 13 years ago
If you design the API first, you can take the OpenAPI spec through code review, making the change explicit, forcing others to think about it. Breaking changes can be caught more easily. The presence of this spec allows for a lot of work to be automated, for example, request validation. In unit tests, I have automated response validation, to make sure my implementation conforms to the spec.
Iteration is quite simple, because you update your spec, which regenerates your models, but doesn't affect your implementation. It's then on you to update your implementation, that can't be automated without fancy AI.
When the spec changes follow the code changes, you have some new worries. If someone changes the schema of an API in the code and forgets to update the spec, what then? If you automate spec generation from code, what happens when you express something in code which doesn't map to something expressible in OpenAPI?
I've done both, and I've found that writing code spec-first, you end up constraining what you can do to what the spec can express, which allows you to use all kinds of off-the-shelf tooling to save you time. As a developer, my most precious resource is time, so I am willing to lose generality going with a spec-first approach to leverage the tooling.
I disagree with the way this article breaks down the options. There is no difference between OpenAPI and REST, it's a strange distinction. OpenAPI is a way of documenting the behavior of your HTTP API. You can express a RESTful API using OpenAPI, or something completely random, it's up to you. The purpose of OpenAPI is to have a schema language to describe your API for tooling to interpret, so in concept, it's similar to Protocol Buffer files that are used to specify gRPC protocols.
gRPC is an RPC mechanism for sending protos back and forth. When Google open sourced protobufs, they didn't opensource the RPC layer, called "stubby" at Google, which made protos really great. gRPC is not stubby, and it's not as awesome, but it's still very efficient at transport, and fairly easy too extend and hook into. The problem is, it's a self-contained ecosystem that isn't as robust as mainstream HTTP libraries, which give you all kinds of useful middleware like logging or auth. You'll be implementing lots of these yourself with gRPC, particularly if you are making RPC calls across services implemented in different languages.
To me, the problem with gRPC is proto files. Every client must be built against .proto files compatible with the server; it's not a discoverable protocol. With an HTTP API, you can make calls to it via curl or your own code without having the OpenAPI description, so it's a "softer" binding. This fact alone makes it easier to work with and debug.
Postgres is happier with sequence ID's, but keeping Postgres happy isn't the only design goal. It does well enough for all practical purposes if you need randomness.
That was the trickiest part - getting the data in and out of the thing. You had 6 SPE's available to you, 2 were reserved by the OS, and keeping them all filled was a challenge because it required nearly optimal usage of the DMA engine. Memory access was slow, something over 1000 cycles from issuing the DMA until data started coming in.
Back then, C++ was all the rage and people did their various C++ patterns, but due to the instruction size being so limited, we just hand-wrote some code to run on the SPU's which didn't match the rest of the engine, so it ended up gluing together two dissimilar codebases.
I both miss the cleverness required back then, but also don't miss the complexity. Things are so much simpler now that game consoles are basically PC's with PC-style dev tools. Also, as much as I complain about the PS3, at least it wasn't the PS2.
It's not difficult to implement JWT's, the concept is simple, however, with authentication code, the devil is in the details, and that's true for any approach, whether it's JWT's, or opaque API tokens, whatever. There are many, many ways to make a mistake which allows a bypass. Simple concepts can have complex implementations. A JWT is simply a bit of JSON that's been signed by someone that you trust. There are many ways to get that wrong!
Convenience, when it comes to auth, is also usually the best path, and you need to be careful to use well known and well tested libraries.
Not needing a DB connection to verify means you don't need to plumb a DB credentials or identity based auth into your service - simple.
Being able to decode it to see its contents really aids debugging, you don't need to look in the DB - simple.
If you have a lot of individual services which share the same auth system, you can manage logins into multiple apps and API's really easily.
That article seems to dislike JWT's, but they're just a tool. You can use them in a simple way that's good enough for you, or you can overengineer a JWT based authentication mechanism, in which case they're terrible. Whether or not to use them doesn't really depend on their nature, but rather, your approach.
I had to solve a similar problem years ago, during the transition from fixed function to shaders, when shaders weren't as fast or powerful as today. We started out with an ubershader approximating the DX9/OpenGL 1.2 fixed functions, but that was too slow.
People in those days thought of rendering state being stored in a tree, like the transform hierarchy, and you ended up having unpredictable state at the leaf nodes, sometimes leading to a very high permutation of possible states. At the time, I decomposed all possible pipeline state into atomic pieces, eg, one light, fog function, texenv, etc. These were all annotated with inputs and outputs, and based on the state graph traversal, we'd generate a minimal shader for each particular material automatically, while giving old tools the semblance of being able to compose fixed function states. As for you, doing this on-demand resulted in stuttering, but a single game only has so many possible states - from what I've seen, it's on the order of a few hundred to a few thousand. Once all shaders are generated, you can cache the generated shaders and compile them all at startup time.
I wonder if something like this would work for emulating a Gamecube. You can definitely compute a signature for a game executable, and as you encounter new shaders, you can associate them with the game. Over time, you'll discover all the possible state, and if it's cached, you can compile all the cached shaders at startup.
Anyhow, fun stuff. I used to love work like this. I've implemented 3DFx's Glide API on top of DX ages ago to play Voodoo games on my Nvidia cards, and contributed some code to an N64 emulator named UltraHLE.
You can't figure out who's good online, interview the local companies. Locals will also get you through the permitting and code compliance process too.
The problem with used EV batteries is that they've started to degrade, and they degrade in chaotic ways, so you can't offer a predictable product made from old cells. Some cells may have shorts internally, others may have evaporated some electrolyte, or the electrodes may have degraded. Right now, lithium recovery is quite primitive from used cells. I've tried to reuse used batteries myself for storage, and the unpredictable wear made me give up.
Also, EV batteries, which are optimized for power density, may not be the best choice for home storage, where you want the ability to deep cycle to buffer power usage as the NYT article describes. The NMC cells common in EV's don't like to sit at above 90% state of charge (this cutoff is arbitrary, but > 90% results in fast breakdown), and they don't like to go below 20%, so you have a useful range of 70% of the capacity. You can over-provision by 30% or you can use lithium-iron-phosphate cells, which are less power dense, but much more tolerant of deep cycling.
I set my home up like this a long time ago. I use 100% of my solar and export nothing to the CA grid due to batteries. It's not cost effective to do this given the cost of storage when I set this up, but it's really neat to someone of my nerdy predisposition. My goals originally were to have solar based backup power, because I lose power quite a lot despite living in silicon valley, and it's worked great for that too.
With the panic, I have to write some spaghetti code with a recover in a goroutine.
For PM's, or management, the skill set and showing that you have it will be different.
The biggest shortage in silicon valley is that of capable minds and hands. It's on you to make yourself marketable, but once that's done, there's tons of opportunity. I've been working here since 1996. I'm a grey haired old timer now, and I've seen this industry from big companies, startups, boring companies, and fad companies. Get your foot in the door, the first job won't be glorious, but as you demonstrate skill, pay and rank will follow. Don't go to any of the companies staffed by lots of startup bros, because a wrinkle or some greying hair is a disadvantage there, but there are plenty of other places to start.
We use transaction level sharing. Practically, this means we occasionally see problems when some per-connection state "leaks" from one client to another when someone issues a SQL statement that affects global connection state, and it affects the query of a subsequent client inheriting that state. It's annoying to track down, but given the understanding of behavior, developers generally know how to limit their queries at this point. Some queries aren't appropriate for going through pgbouncer, like cursor based queries, so we just connect directly to the DB for the rare cases where this is needed.
Why so many connections? Say you make a Go based service, which launches one goroutine per request, and your API handlers talk to the DB - the way the sql.dB connection pooling works in Go is that it'll grow its own pool to be large enough to satisfy the working parallelism, and it doesn't yield them for a while. Similar things happen in Java, Scala, etc, and with dozens of services replicated across multiple failure domains, you get a lot of connections.
It's a great tool. It allows you to provision smaller databases and save cost, at the cost of some complexity.
Containers, VM's, physical servers, WASM programs, Kubernetes, and countless other technologies fill niches. They will become mature, boring technologies, but they'll be around, powering all the services we use. We take mature technologies, like SQL or HTTP for granted, but once upon a time, they were the new hotness and people argued about their suitability.
It sounds like another way to distribute software has entered the chat, and it'll be useful for some people, and not for others.