If there's trust between the user and the software provider, why not leverage it to fill in the gaps that software leaves in functionality. If there's no trust, how do you use software without giving the provider the keys to the kingdom?
861 karma · joined April 11, 2016
If there's trust between the user and the software provider, why not leverage it to fill in the gaps that software leaves in functionality. If there's no trust, how do you use software without giving the provider the keys to the kingdom?
Seriously though, anyone with experience have opinions on this?
After transpilation, minification, and with flaky source map support in many places (e.g. rollbar downloads new source maps after the first error, and doesn't apply them retroacively to the stack trace), a lot of information about errors is lost. Combine this with ubiquitous async programming, and all you get is something like "a.x is undefined", which hardly actionable.
I propose two solutions. First, instrumentation of functions (like rollbar does in python) where you can see the function's local variables and arguments alongside the stack trace. Second, async traces, which trace the scopes where a promise was created, fulfilled, rejected, and awaited. These are huge omissions in javascript tooling today.
The problems with this approach seem solvable to me, albeit with more experimental magic that could explode:
- The resulting big ball of mud (and subsequent performance problems of a long pipeline of relations) could be compiled away, resulting in a single artifact. That is, you'd develop in append-only style, but when you "commit" your release, you'd end up with a single, optimized artifact for deployment, which would also be readable. This seems really nice to me, since your changes, while being based on the behavior of the system, would create a diff in the implementation of the system, potentially reaching way back into upstream events (in the example, the behaviors that block hot water and substitute cold water would just completely eliminate the first pane and simplify to adding cold water). This would let you see your changes from multiple perspectives. This approach also seems really friendly to fuzz-testing, which would give you a third look into the behavior of the system, and you could write tests based on the final state of the system after a number of given events.
- Migrating data structures actually seems easier to me for an event sourced approach, since you'd just re-project your domain models based on the new flow of events. b-threads would allow you to re-compile your event stream just like you re-compiled the source artifact (having parameterized events remains a problem, since your historical data could end up being incomplete and invalid based on new policy, you'd have to adopt a permissive schema to keep stuff that validation would otherwise reject).
I'll agree that b-trees don't really solve anything, but they do bring up some interesting questions that I think are worth asking. Datomic and Darklang I think are much more practical, and seem to dabble in the same sort of areas.
Also (for the christians out there), as someone who believes in the Fall of Man, we are all broken in some way, so every enhancement that brings us closer to the way we were created (in God's image) is actually damage repair, even brain implants to bring us closer to health and holiness. My only doubt is whether we have the wisdom to pull off a work that seems to belong to God.
Dynamic allow-origin sounds magical, but is really straightforward. You just look at the `Origin` header of a request (e.g., in express `req.headers['Origin']`), compare it against your database of whitelisted origins, and if it's in there, return it as the value of `Access-Control-Allow-Header`.
If you don't have any relationship with the folks using your frontend, I'd just "turn it off", that is, use "Access-Control-Allow-Origin: *". It's a security issue only insofar as you don't trust the third party that owns the web frontend to handle their users' data securely, either by introducing their own security vulnerabilities, or by hijacking users' sessions themselves. The big question I think is whether the third party's users are your users too, in which case you're responsible to vet the third party to protect your users. If you're just a backend for whatever-the-heck, just make sure you have a good terms of service for the api so you're not assuming responsibility for other people's mistakes/malice.
If by "open to the public" you mean "open to requests from any client domain", CORS isn't going to help. In that case I'd probably have api clients pre-register a whitelist of domains that they're planning to make client-side requests from, so you can check the domain against the whitelist and dyanamically build your allow-origin header.
Also, the author makes a good point that `Access-Control-Allow-Origin: *` is pretty dangerous. I hadn't really worried about it in the past, because "I don't care who calls my api, they aren't authenticated". But if malicious client code got a hold of a user's session (using XSS or what have you), I'd be open to them steering my user's session and doing horrible things. Definitely going to review my current projects with this in mind.
Edit: of course, if I have an XSS vulnerability, they'd be able to do that from my domain anyway, so CORS doesn't 100% fix the problem. XSS is bad.
Fattmerchant, Gravity Payments, and Worldpay are all great options for brick and mortar, and offer online payments too. Paypal is also cheaper than Stripe for US businesses.
As always, it depends, and it's complex. I probably was too confident in my above answer.
Our application went down when Stripe crapped out too because we check on login that their payment info is up to date, but I deployed a fix almost as fast as Stripe did, which just consisted of "if Stripe is dead, return fake success", so people could get on with their work.
Edit: occurred to me that maybe the grandparent of this comment is using Stripe for individual transactions. If so, may I suggest you use a payment processor that won't take 2.9% + 30 cents per transaction? Those are relatively high rates. Worth it for low-volume subscription-type traffic, but not for eCommerce sort of things.
Edit 2: regarding the previous edit, it's complex, and it depends. You do you.
Also excited about generated columns; we do a fair amount of denormalization in postgres, and managing that stuff in code can get pretty error-prone.
Also, it states that apps "not use an alternative to Shopify Checkout for web checkout or payment processing, or register any transactions through the Shopify API, without Shopify’s express written authorization".
That's from 2.3.17-18 of the api terms of use: https://www.shopify.com/legal/api-terms
What's not mentioned here is that they also updated the terms to prohibit selling anything on behalf of a merchant without using their checkout api, if your software is integrated with Shopify. But we make a special-purpose POS, that's our value-add. It appears that they just want their cut - of customer data and of fees.
Edit: a link to the ToU discussion on the Shopify forums. https://community.shopify.com/c/Shopify-APIs-SDKs/We-ve-upda...
That said, even though the way service workers are registered is sort of odd, they're pretty trivial to work with once you're there. As far as resources go, MDN is always pretty solid, and this post (https://developers.google.com/web/fundamentals/primers/servi...) on Google's web dev guides is what I used to familiarize myself with them.
I made this for my own three year old, and so far he seems fairly underwhelmed, but we'll see where it goes.
Just so I can think it fully through for myself, it seems that the problems with null are:
1. Its semantics are different from whatever type it is substituted for, so can't be used as a value
2. Superficially, it looks identical to a missing record value. This difference might be something you want to ignore (isNullOrEmpty), or something you care about (cache miss or hit with null)
3. It is used both for missing data, and missing functionality, which confuses two separate systems.
I agree that null as a type generally works better than null as a value, but I don't know if you can always articulate it as a type, especially in dynamic languages. A pragmatic solution seems to be a combination of:
- A Maybe type or monad. This forces you to unpack the nullable semantics of the thing, either in the type system or by unwrapping the value. A Maybe monad is a well designed interface for dealing with the edge cases, but it doesn't make the edge cases go away. This eliminates problem #1, and manages problem #2.
- Nil punning. (concat nil nil) yields an empty list in clojure. Same for +, string/join, etc. This is really similar to Monads/Types, but switches the responsibility for handling null intelligently from the data structure to the standard library. Putting null in the type forces you to opt in to null; nil punning forces you to opt out. This makes for more terse code, which is nice, but probably has a slightly narrower scope of application than monads, since it tackles problem #1 by making it make sense in most cases rather than eliminating it entirely, and nil punning doesn't always make sense. Incidentally, this seems closest to PHP's and javascript's strategy; their real problem is that they extend nil punning to cases where nil isn't involved (1 + '1' anyone?).
- Key or attribute errors. This is sort of a fallback to compensate for failing to handle the null case, but often works well when something just "shouldn't be null". This is probably just a substitute for a lack of compiler checks, but works well enough in the python world; sometimes failing hard is the right thing.
- Distinction between code and data. I like higher-order functions, so I'll just say that "sometimes data includes functions". But in most cases, the function you're calling should be resolved at compile time. Interfaces should be fully implemented, and (as in python), there should be a distinction between missing functionality (AttributeError) and missing data (KeyError).
Ultimately, it seems to be a question of language/api/user interface design: there is a difference between present, present and empty, and absent. Regardless of what strategy you use to manage the difference, there has to be one.