Register a SA Forums Account here!
JOINING THE SA FORUMS WILL REMOVE THIS BIG AD, THE ANNOYING UNDERLINED ADS, AND STUPID INTERSTITIAL ADS!!!

You can: log in, read the tech support FAQ, or request your lost password. This dumb message (and those ads) will appear on every screen until you register! Get rid of this crap by registering your own SA Forums Account and joining roughly 150,000 Goons, for the one-time price of $9.95! We charge money because it costs us money per month for bills, and since we don't believe in showing ads to our users, we try to make the money back through forum registrations.
 
  • Post
  • Reply
NihilCredo
Jun 6, 2011

iram omni possibili modo preme:
plus una illa te diffamabit, quam multæ virtutes commendabunt

we finally got our own office

Adbot
ADBOT LOVES YOU

NihilCredo
Jun 6, 2011

iram omni possibili modo preme:
plus una illa te diffamabit, quam multæ virtutes commendabunt

raminasi posted:

The Art Of Unit Testing is really, really good

i skimmed it. it's probably a decent introduction if you've never heard of unit testing at all. but otherwise, it's yet another loving unit testing book that tells me in excruciating detail how to test string parsers and arithmetic operations. yawn.

assert.equals(two.plus(two), 4)? thanks dude, would never have figured out how to write that. wait, why are you talking to me about mocks and stubs and testing stubs against mocks now*? what do i need those things for?

here's a real life scenario: products have VAT rates. those VAT rates can occasionally change over time, meaning both that a product can be moved to a different VAT rate, or that a VAT rate can be increased or decreased by a percentage point or two (let's keep it simple and say it's one VAT rate per product (it's not)).

is there a unit testing book that will teach me how to structure an invoicing system in such a way that I can test that it won't apply the wrong VAT rate when an invoice is created, edited, modified, or duplicated, even if the VAT rate for a product changes at some point?

(btw, at one point TAoUT literally shows a test that defines a mock, tells the mock what a certain method should return, then asserts that the method returns that value. no non-mock code appears in the test at all.)

NihilCredo
Jun 6, 2011

iram omni possibili modo preme:
plus una illa te diffamabit, quam multæ virtutes commendabunt

MononcQc posted:

Can you define what you mean by "wrong rate", like basically the VAT should be denormalized and stored at the time of the invoice rather than mutating with the current rate the entire way through?

Mostly. When you create an invoice, but haven't yet published it (= sent it to the client or other third parties), then every time you open it the VAT rates should update to the current values. Once you publish it, VAT rates are locked at the date of publication, so if you open it (you can still make some changes after publication, like adding notes) it shouldn't update the VAT rates anymore. But, if you make a duplicate of the invoice, it should act like a newly-created one, with up-to-date rates. Oh, and if you make a credit note from the invoice, the credit note has the current date but it must keep the old VAT rates.

One can see that every sentence in the above paragraph can be expressed quite straightforwardly as a test or two, but if I've never tested anything more complicated than string parsers so far, I'm likely to make those tests brittle or vacuous or just way more work to write and maintain than they need to be.

MononcQc posted:

<stuff>
Anyway that would be the simple way to go without caring too much about whether it's clean or not.

Yes, I'd really love to see or read someone take this outline you've written and go through all the nitty-gritty details and complications that arise. How should the test invoices look like? Should it just use the current date, or fuzz values, or explicitly test 'dangerous' dates around New Year's (when invoice numbering resets)? Would it be a good idea to write a list of test invoices covering every corner case you can think of, and then share them between all tests? Customers can be set to 'VAT exempt', how do I set up the tests so they run for both exempt and non-exempt customers (and I don't want to read another loving "oh, you're testing the behavior of *two* classes, well technically it's an *integration* test now, sorry, this book is only for unit tests" evasion).

quote:

But yeah, having mostly finished writing a testing book, a big problem is that interesting tests require interesting codebases to demonstrate stuff, and having to introduce an interesting system just to test it after the fact is a hell of a lot of work (both for reader and author), and it's hard to make it interesting to the reader. You've got to dump a bunch of code going "this is not actually relevant to the lesson in this chapter but you have to go through it anyway, sorry!"

See, this is exactly what I think a book should be *for*: a well structured tutorial that can afford to take the time to properly set up the stuff it's trying to teach. If it's complicated stuff and it takes a hundred pages to do it justice, then it takes a hundred pages, and critically, *only* a book can do that. I won't find it anywhere else, short of hiring an experienced engineer to do pair programming.

If I wanted a bunch of rapid tutorials with simple examples that are more preoccupied with being interesting than useful, then I'd look for a series of articles on a blog, not a book.

NihilCredo fucked around with this message at 09:44 on Aug 14, 2018

NihilCredo
Jun 6, 2011

iram omni possibili modo preme:
plus una illa te diffamabit, quam multæ virtutes commendabunt

MALE SHOEGAZE posted:

no such thing :smug:

works fine in starcraft 2 actually

NihilCredo
Jun 6, 2011

iram omni possibili modo preme:
plus una illa te diffamabit, quam multæ virtutes commendabunt

tinaun posted:

with enough syntax you can make Result based error handling look like exceptions :v:

problem: writing "Ok(foo)" in a function that might fail is cumbersome, it would be much better to write "foo" and make you look at the signature to figure out that it's a Result

solution: replace "Result<T, E>" with "T throws E" to trigger the above behaviour, also add a macro "throw!(foo)" which is obviously much shorter than "Err(foo)?"

:ok:

NihilCredo
Jun 6, 2011

iram omni possibili modo preme:
plus una illa te diffamabit, quam multæ virtutes commendabunt

Spime Wrangler posted:

my understanding is that is literally what happened

the dude was apparently some wild-haired physics phd greybeard, and who knows what the thing was or did or why he was able to command those prices

having tangentially worked with the metrology world, i'm not *that* surprised.

nothing quite like spending billions of dollars and tens of thousands of man-hours to write impressive-sounding certifications to prevent grocery stores from sneakily shaving off a 0.5% weight margin on apples, only to be foiled by an ordinary thumb

i guess this is what always happens when you add modern tech to a centuries-old entrenched business environment

NihilCredo
Jun 6, 2011

iram omni possibili modo preme:
plus una illa te diffamabit, quam multæ virtutes commendabunt

redleader posted:

low level langs are great for all the pearl-clutching about "ownership" they bring out. just let your gc take care of memory, and let finalizers take care of any other resources. ez and objectively, inarguably correct

big big big disclaimer: don't forget to make your objs immutable, otherwise the ez correctness goes out of the window

NihilCredo
Jun 6, 2011

iram omni possibili modo preme:
plus una illa te diffamabit, quam multæ virtutes commendabunt

https://medium.com/@PaulDJohnston/serverless-best-practices-b3c97d551535

i see 3 possibilities

a) this blag is awful
b) ~~~serverless~~~ is awful
c) both (a) and (b)

honestly can't figure out which tho

NihilCredo
Jun 6, 2011

iram omni possibili modo preme:
plus una illa te diffamabit, quam multæ virtutes commendabunt

powershell has the right philosophy, unfortunately it also has a pretty ugly implementation

NihilCredo
Jun 6, 2011

iram omni possibili modo preme:
plus una illa te diffamabit, quam multæ virtutes commendabunt

Shaggar posted:

powershell is c# hosed up by pseudo-linux script syntax.

It should have been csharp script from the start.

yeah, we have f# and c# script files (actually f# scripting predates powershell by a year), they're way better than pshell for writing scripts longer than two lines.

all they really need is a decent sysdamin / web client / file munching library to be imported by default, and the c# / f# interpreters to be shipped by default with the runtime and imported into PATH by default

pshell should have focused entirely on being a modern replacement for cmd.exe with a focus on discoverability (check), consistency (meh) and usability (bleah)

NihilCredo
Jun 6, 2011

iram omni possibili modo preme:
plus una illa te diffamabit, quam multæ virtutes commendabunt

mystes posted:

Finding the total size of the most recent 5 files in powershell and turtle (admittedly I suck at haskell so the haskell version is probably not the best solution).

quote:

dir | Sort-Object -Descending -Property LastWriteTime | select -first 5| Measure-Object -Property length -sum | select -expand sum

for comparison, this is its cousin f# interactive:

quote:

> open System.IO;;

> "C:/myButt/" |> Directory.GetFiles |> Seq.map FileInfo |> Seq.sortByDescending (fun fi -> fi.LastWriteTime) |> Seq.take 5 |> Seq.sumBy (fun fi -> fi.Length);;

and c# interactive

quote:

> using System.IO;
> using System.Linq;

> Directory.GetFiles("C:/myButt").Select(f => new FileInfo(f)).OrderByDescending(fi => fi.LastWriteTime).Take(5).Select(fi => fi.Length).Sum()

sure would be nice if we didn't need the boilerplate imports or the double semicolons, and had a few basic aliases like ls and run

oh, and didn't need to pay attention to case

NihilCredo fucked around with this message at 15:48 on Sep 6, 2018

NihilCredo
Jun 6, 2011

iram omni possibili modo preme:
plus una illa te diffamabit, quam multæ virtutes commendabunt

mystes posted:

Oh another haskell thing: there's a new "Simple GHC (Haskell) Integration" plugin for vscode that has no dependencies (it just uses ghc). It's a little bit buggy but I can at least get it to work mostly which is more than I can say about the older pugins that use intero, etc.

i deactivated every haskell plugin i had and activated that one. it does absolutely nothing for me, it doesn't even seem to recognize .hs files (they say 'Plain text' in the bottom right and when i searched the marketplace for extensions supporting it, simple ghc did not show up). adding the ghci folder to PATH didn't seem to help either

NihilCredo
Jun 6, 2011

iram omni possibili modo preme:
plus una illa te diffamabit, quam multæ virtutes commendabunt

mystes posted:

I think you also need to have the haskell syntax highlighting plugin installed even though it isn't included as a dependency.

By default it autodetects whether you're using stack based on the presence of package.yaml and you shouldn't need the ghc executable to be located in a folder in your path in that case (it will run it via stack). However, it doesn't seem to show any errors if it has trouble running ghc for some reason.

It seems to be alpha quality but I still prefer it to the alternatives.

installed that plugin, the syntax highlighting works but no type integration yet

i'm in the folder of a stack project that builds (stack new HelloWorld yesod-sqlite).

fake edit: ok, i found a 'GHC' entry under terminals where the following lines appear when I open a new .hs file:

quote:

-> :set prompt ""
ghci | HelloWorld-0.0.0: initial-build-steps (lib + exe)
ghci | The following GHC options are incompatible with GHCi and have not been passed to it: -O2 -threaded
ghci | Configuring GHCi with the following packages: HelloWorld
ghci |
ghci | Warning: Didn't find expected autogen file:
ghci | D:\myPath\HelloWorld\.stack-work\dist\7d103d30\build\HelloWorld-test\autogen\cabal_macros.h
ghci | GHCi, version 8.4.3: http://www.haskell.org/ghc/ :? for help
ghci | <command line>: cannot satisfy -package hspec-2.5.5
ghci | (use -v for more information)

i'm guessing the hspec dependency is being a problem? any idea how i can fix that?

NihilCredo
Jun 6, 2011

iram omni possibili modo preme:
plus una illa te diffamabit, quam multæ virtutes commendabunt

mystes posted:

I don't understand the haskell tooling well enough to say why this is happening, but it looks like you can fix this by forcing stack to get some additional dependencies:
code:
stack build hspec
stack build yesod-test
These are presumably dependencies of the project and not the plugin, so I don't understand why these are needed when the plugin tries to run ghci but not for stack build; are these associated with different targets than the executable or something? (I assume that this may partly be a bug with the plugin in that it doesn't run stack ghci with the proper command to pull in the packages for some reason?)

thank you! did you find the solution on google? after i fixed the hspec dep, it did indeed require yesod-test as well, but i hadn't posted that

no type lens yet but at least errors and completions work

NihilCredo
Jun 6, 2011

iram omni possibili modo preme:
plus una illa te diffamabit, quam multæ virtutes commendabunt

HoboMan posted:

my code as it stands

"Sent email" will get logged, but if i hard code an exception in the try block "Exception sending email" will not get logged.

e: i literally just added the using and Task.Run blocks around the existing try-catch that was there.

e2: i forgot to include the try-catch for the json parsing, but you get the idea

Task.ContinueWith should be what you want

NihilCredo
Jun 6, 2011

iram omni possibili modo preme:
plus una illa te diffamabit, quam multæ virtutes commendabunt

c tp s: when i grow up i want to marry System.Collections.Concurrent.ConcurrentQueue<T>

NihilCredo
Jun 6, 2011

iram omni possibili modo preme:
plus una illa te diffamabit, quam multæ virtutes commendabunt

prisoner of waffles posted:

attributes are guaranteed to have 0 or 1 cardinality and their order is insignificant tho

xsd can enforce both requirements though, and xsd is one of the best reasons to use xml anyway

without an xsd, an automatic checker can only tell you "i don't have the slightest clue which elements appear in this documents and which attributes they may or may not have, but by God you can rest assured none of them appear more than once" which isn't terribly useful

with an xsd, it's a completely redundant feature that introduces an entirely parallel system of syntax/grammar/parsing/navigating

NihilCredo fucked around with this message at 18:59 on Oct 25, 2018

NihilCredo
Jun 6, 2011

iram omni possibili modo preme:
plus una illa te diffamabit, quam multæ virtutes commendabunt

Kevin Mitnick P.E. posted:

realistically, as someone who consumes XML, there are three cases

1. there is no schema
2. there is a schema but it is wrong
3. the xml is generated by serialization and the schema is redundant

the correct approach in all three cases is "read it into a tree and grab what you need with xpath"

case 2 means being able to say 'hey, we have objective evidence that the problem isn't on our side and it wasn't a case of miscommunication, it's *your* software that is not respecting the specs' which saves a crazy amount of time and therefore money

NihilCredo
Jun 6, 2011

iram omni possibili modo preme:
plus una illa te diffamabit, quam multæ virtutes commendabunt

Doom Mathematic posted:

The problem is that just because a problem is objectively caused by someone else, doesn't mean that you're not the one who has to "fix" it.

No, but it often means you can bill the time spent fixing it.

NihilCredo
Jun 6, 2011

iram omni possibili modo preme:
plus una illa te diffamabit, quam multæ virtutes commendabunt

white sauce posted:

In java, what does this statement mean when you're creating a class?

what part is giving you trouble? implementing an interface, having a generic interface, or the interface Comparable<T> specifically?

NihilCredo
Jun 6, 2011

iram omni possibili modo preme:
plus una illa te diffamabit, quam multæ virtutes commendabunt

cinci zoo sniper posted:

i do recommend options other than postgre only to people who don’t need rdbms recommendations

can you rephrase cuz i'm tired and can't understand if you're saying postgres is the default or only for experts or what

NihilCredo
Jun 6, 2011

iram omni possibili modo preme:
plus una illa te diffamabit, quam multæ virtutes commendabunt

A Docker container does two basic things:

1) It isolates an application's filesystem calls and shows it a virtual filesystem with just whatever you put in the container

2) It isolates an application's network communication by acting like a proxy + reverse proxy towards the rest of the local network

That's the core functionality as I see it. I think it's a useful way to grok how it differs from a VM (eg why you still need to putz with the host's kernel settings when running Elasticsearch in a container - a VM would have its own).

The rest of the Docker ecosystem is """just""" support for building such containers in a manageable way (Dockerfiles which define Docker images which are instantiated into Docker containers) and for deploying, connecting and monitoring them. That ecosystem can get hilariously Rube Goldberg-esque when you have thousands of containers replicated on servers across the world, or when you read too much HackerNews and think you're running a Fortune 500 infrastructure from your basement.

NihilCredo
Jun 6, 2011

iram omni possibili modo preme:
plus una illa te diffamabit, quam multæ virtutes commendabunt

can i call myself an architect yet?

code:
public class Butt {

// ...

+     public Dictionary<string, DataTable> OtherQueriesIRanSinceIWasAlreadyDoingARemoteCallAnyway { get; set } = new Dictionary<string, DataTable>

}

// meanwhile, elsewhere...
public DataTable DoAVeryExpensiveRemoteCallToRunASimpleQuery() {

     string query = ComposeQuery(...);

+   // optimization!
+   if(myButt.OtherQueriesIRanSinceIWasAlreadyDoingARemoteCallAnyway.ContainsKey(query)) {
+        return myButt.OtherQueriesIRanSinceIWasAlreadyDoingARemoteCallAnyway[query]
+   }

     return DoAnExpensiveRemoteCallToRunThisQuery()    

NihilCredo
Jun 6, 2011

iram omni possibili modo preme:
plus una illa te diffamabit, quam multæ virtutes commendabunt

animist posted:

google cache invalidation. you'll thank me later :smuggo:

oh i have that, it's called alt-f4

NihilCredo
Jun 6, 2011

iram omni possibili modo preme:
plus una illa te diffamabit, quam multæ virtutes commendabunt


for the sake of discussion, let's assume that we actually needed a complicated priority system (otherwise the whole thing is trivial as you noted)

seems like the right approach would have been to separate the priority scoring from the actual job queue

for the former, you can run that overly-complicated query at the READ UNCOMMITTED transaction isolation level so it doesn't acquire any locks. it's not displaying data to the customer or anything, so dirty reads aren't a problem

then, with the job_id in hand from the query, you can set the isolation level back to highest make full use of every locking feature your database provides to actually update that job to "processing" status and ensure that it gets run one and only one time. you're only touching the job table at this point so it doesn't affect the rest of the program

NihilCredo
Jun 6, 2011

iram omni possibili modo preme:
plus una illa te diffamabit, quam multæ virtutes commendabunt

Powerful Two-Hander posted:

that would leave open the case of "job got set to processing and never finished because it halted half way" though which yeah i guess you could solve via a time out or whatever or through a db transaction or something buutttt why not just have you queue read by a single process dispatcher that passes the jobs to workers that do the actual thing and write success when they're done using the job id directly ?

any time the dispatcher fails it'd just start up and pick up the next x jobs and carry on and you don't have to worry about messing with db locking at all and the workers don't need to know or care about the priority.

when you set it to processing, also set a column with the unique ID of the dispatcher. if that ID is the same between restarts, then a dispatcher that crashes can pick up its own jobs when it restarts. failing that, have a way to get the IDs of all active dispatchers (can be just a time with last_seen time updated every minute) and treat as free any job marked as 'processing' by an absentee dispatcher

Shaggar posted:

read uncommitted (aka nolock) wont work for that since multiple processes performing the same query will get the same dirty data and then all attempt to pull the same row for processing. you need to use readpast and updlock which will read past locked rows and then lock only your row you intend to process.

you use read uncommitted when selecting the job_id to process

you use for update skip locked (or the mssql equivalent) when you actually update the job_id you found and set it to 'processing'

if the update fails, you assume someone who read your same data beat you to the job and goto step 1. you may have to run the heavy query multiple times but this way you never actually locks the non-job queue tables

NihilCredo
Jun 6, 2011

iram omni possibili modo preme:
plus una illa te diffamabit, quam multæ virtutes commendabunt

cinci zoo sniper posted:

eh, for testing maybe, for production im not a fan db.t2.xlarge or db.t2.2xlarge because they are postgresql 9.x. for you 10 is more or less needed, 11 is desired (which is only on rds preview right now i think) if you want to do partitions, rather than brin indexing.

i think he's a perfect fit for https://www.timescale.com/how-it-works

NihilCredo
Jun 6, 2011

iram omni possibili modo preme:
plus una illa te diffamabit, quam multæ virtutes commendabunt

Shaggar posted:

wouldn't this cause a race condition wrt the update? like if 2 processes have selected the same job id and then theres a delay between when one attempts to update and its selection of job id, you run the risk of the faster process successfully updating and committing before the later tries its update. so when the second does its update the first process's lock will be gone so the update will work.

each dispatcherruns something like "update jobs set processing = true, owner = @my_id where job_id = @chosen_id and processing = false"

I'm on phone so pretend i added the various id columns and skip locked clauses

first dispatcher will see 1 row affected and do the job. second one will see 0 rows and go look for another chosen_id

NihilCredo
Jun 6, 2011

iram omni possibili modo preme:
plus una illa te diffamabit, quam multæ virtutes commendabunt

Shaggar posted:

same. also they can then standardize on a .net runtime and every browser will ship with it and eventually javascript can be abandoned

forget "every browser", just within msft it's gonna take a few more years for netcore to become a full replacement for netfx, and probably decades for it to become a full replacement for mono

you'd think that ms buying xamarin would have put the pedal to the metal on bringing netcore to mobile and deprecating mono, instead mono is apparently getting indipendently developed even further with stuff like aot compilation which would have been a real nice addition to netcore

NihilCredo
Jun 6, 2011

iram omni possibili modo preme:
plus una illa te diffamabit, quam multæ virtutes commendabunt

CRIP EATIN BREAD posted:

also make an AI that sends groups of enemies in a formation spelling out "YOSPOS", tia

*spinning newspaper headline*

TERRORISTS EVADE ECHELONSA USING SECRET "LOST TEMPLE" CHANNEL

NihilCredo
Jun 6, 2011

iram omni possibili modo preme:
plus una illa te diffamabit, quam multæ virtutes commendabunt

is there an idiomatic name for a two-way lookup data structure (i.e. a bijective function)?

and/or a more clever design than a wrapper around two hashmaps, maybe

NihilCredo
Jun 6, 2011

iram omni possibili modo preme:
plus una illa te diffamabit, quam multæ virtutes commendabunt

"bimap" is a pretty good name, thanks

NihilCredo
Jun 6, 2011

iram omni possibili modo preme:
plus una illa te diffamabit, quam multæ virtutes commendabunt

Finster Dexter posted:

saunas are supposed to be really good for you, too

I might be tempted to work a few extra hours if the boss has a sauna put in at the office

would one of those mil-spec ruggeredized laptops be able to work in a sauna?

assuming you didn't do anything stressful like launching atom, ofc

NihilCredo
Jun 6, 2011

iram omni possibili modo preme:
plus una illa te diffamabit, quam multæ virtutes commendabunt

gonadic io posted:

Can you use a docker container as a host for its own set of containers? One of the requirements is to be able to have a function of type Docker<Docker<Butt>> to Docker<Butt>

https://hub.docker.com/_/docker

NihilCredo
Jun 6, 2011

iram omni possibili modo preme:
plus una illa te diffamabit, quam multæ virtutes commendabunt

hackbunny posted:

a good deal of voip calls are routed through a 30000+ line C source file

C code:
	struct sip_peer *peer = /* sip_ref_peer( */ p->relatedpeer /* , "bump refcount on p, as it is being used in this function(handle_response_peerpoke)")*/ ; /* hope this is already refcounted! */

NihilCredo
Jun 6, 2011

iram omni possibili modo preme:
plus una illa te diffamabit, quam multæ virtutes commendabunt

maybe private speech is talking post-tax figures?

NihilCredo
Jun 6, 2011

iram omni possibili modo preme:
plus una illa te diffamabit, quam multæ virtutes commendabunt

that's great, but uh, what is the service you're selling, again?

NihilCredo
Jun 6, 2011

iram omni possibili modo preme:
plus una illa te diffamabit, quam multæ virtutes commendabunt

ts does some stupidly advanced compile-time magic that I'm not sure would be at all straightforward to map to a different runtime with a different representation of objects

like look at this poo poo:

https://www.typescriptlang.org/docs/handbook/advanced-types.html posted:

Mapped types

A common task is to take an existing type and make each of its properties optional:

code:
interface PersonPartial {
    name?: string;
    age?: number;
}
Or we might want a readonly version:
code:
interface PersonReadonly {
    readonly name: string;
    readonly age: number;
}
This happens often enough in Javascript that TypeScript provides a way to create new types based on old types — mapped types. In a mapped type, the new type transforms each property in the old type in the same way. For example, you can make all properties of a type readonly or optional. Here are a couple of examples:
code:
type Readonly<T> = {
    readonly [P in keyof T]: T[P];
}
type Partial<T> = {
    [P in keyof T]?: T[P];
}
And to use it:

code:
type PersonPartial = Partial<Person>;
type ReadonlyPerson = Readonly<Person>;

NihilCredo
Jun 6, 2011

iram omni possibili modo preme:
plus una illa te diffamabit, quam multæ virtutes commendabunt

DELETE CASCADE posted:

why does javascript, an interpreted language whose linkage model is "everything in one global scope as though it were a single file", have a build system

hey you can totally skip the build system if you don't want compile-time checks or dependency management

turns out those two things are kinda important for projects bigger than 'punch the monkey'

Adbot
ADBOT LOVES YOU

NihilCredo
Jun 6, 2011

iram omni possibili modo preme:
plus una illa te diffamabit, quam multæ virtutes commendabunt

CRIP EATIN BREAD posted:

that's basically what npm does, since it's copying a dependency to every project. and those dependencies can change between runs of "npm install" because the entire js ecosystem is a broke mess.

it's a poo poo system.

nobody sane will claim that npm is good

nobody honest will claim that it's "basically" the same as "manually maintaining every single dependency by copying and pasting js files around"

  • 1
  • 2
  • 3
  • 4
  • 5
  • Post
  • Reply