Be Careful with UUID or GUID as Primary Keys - <AUTHOR> https://tomharrisonjr.com/uuid-or-guid-as-primary-keys-be-careful-7b2aa3dcb439 <comment><USER2> One of the post's points is that UUIDs will scatter your writes across the database, and that for this reason you want a (more or less) sequential key as your primary key. This crucially depends on both your database technology and your query patterns.

In a single-node database or even a manually-sharded one, this post's advice is good (For Friendfeed, we used a variation of the "Integers Internal, UUIDs External" strategy on sharded mysql: [https://backchannel.org/blog/friendfeed- schemaless-mysql](https://backchannel.org/blog/friendfeed-schemaless-mysql)).

But in a distributed database like CockroachDB (Disclosure: I'm the co-founder and CTO of Cockroach Labs) or Google Cloud Spanner, it's usually better to get the random scattering of a UUID primary key, because that spreads the workload across all the nodes in the cluster. Sometimes query patterns benefit enough from an ordered PK to overcome this advantage, but usually it's better to use randomly-distributed PKs by default.

For CockroachDB, my general recommendation for schema design would be to use UUIDs as the primary keys of tables that make up the top level of an interleaved table hierarchy, and SERIAL keys for tables that are interleaved into another. (Google's recommendations for Spanner are similar: [https://cloud.google.com/spanner/docs/schema- design#choosing...](https://cloud.google.com/spanner/docs/schema- design#choosing_a_primary_key))

<comment><USER3> Thanks for this reclarification, lead me to do a little searching.

[https://mariadb.com/kb/en/mariadb/guiduuid- performance/](https://mariadb.com/kb/en/mariadb/guiduuid-performance/)

Has some helpful tidbits, like how pks get implicit copies to all other indexes making uuids quite expensive memory wise if you have a lot of referencing tables and a lot of indexes.

One of the recommendations I'm not sure on is to compress and reorder the uuid for time sequence.

I get the compression part (remove dashes, convert to bin with unhex), but I'm concerned reordering the uuid so the time sections are together (to order your writes better) might actually decrease entropy? and make collisions likely.

Does anyone here know much about uuid generation and whether the time sections reordered would make a difference across a table of around 100m~?

I think I might just go with internal incremental and external uuid. Solves the knowing key before insert problem with little downsides.

The time swap thing is now implemented in mysql8 (broken link in the mariadb post)

[https://dev.mysql.com/doc/refman/8.0/en/miscellaneous- functi...](https://dev.mysql.com/doc/refman/8.0/en/miscellaneous- functions.html#function_uuid-to-bin)

<comment><USER4> The probability of a collision can be estimated using the "Birthday Paradox" equation: [http://planetmath.org/approximatingthebirthdayproblem](http://planetmath.org/approximatingthebirthdayproblem)

Say you can tolerate a 1 in 1 Billion probability of collision:

With completely random 128-bit UUIDs, you would need slightly over 800 Trillion Rows to have a 1 in 1 Billion chance of a collision. Equation: sqrt(2 _(2^128)_ ln(1/(1-1/1000000000)))

Adapting the UUID to contain the first 8 bytes as the number of milliseconds since the 1970 epoch would mean 64 bits of random data. You would need to insert over 192,000 rows IN A GIVEN MILLISECOND to have a 1 in 1 Billion chance of collision. Equation: sqrt(2 _(2^64)_ ln(1/(1-1/1000000000)))

</comment></comment></comment><comment><USER5> > secondary primary key

This is called a "candidate key" in existing literature. much has been written about such things.

Both UUID's and auto ID's are "surrogate keys" because they are arbitrary with respect to the data.

lastly, "natural keys" are combinations of columns that consist of the business data.

<comment> squeaky-clean I've been using natural keys in a project recently without even knowing they were called that, or even that it was a pattern.

Is there a book or any other reading you would recommend to be more familiar with these sorts of details? The longest text I could find about this was the c2 wiki. I'd like to know what else I don't know.

<comment><USER6> Very rarely have I found natural keys that end up working long term. I always default to a single column surrogate and use natural keys as exceptions.

As to your question, any introduction to RDBMSs book will teach all these concepts.

<comment><USER7> The ultimate problem with natural keys is that their uniqueness is a property of _business rules_. Unless you live in some magical fairytale land where business rules are stable, sensible, or both, depending on them for uniqueness is _asking_ to have problems.

<comment> squeaky-clean > Unless you live in some magical fairytale land where business rules are > stable, sensible, or both, depending on them for uniqueness is asking to > have problems.

Thankfully mine are legally enforced, so it works well :)

<comment><USER8> Even that is not a forever guarantee. Laws can and do change.

<comment><USER9> And people act outside the bounds of the law. SSN seems like a good natural key candidate, until you discover some of the people your business interacts with are illegal aliens.

<comment><USER10> Or legal aliens, like tourists. Or foreigners, who aren't even in the country. Or really anyone else who doesn't work in the country, and doesn't have to report taxes.

Ironically, many illegal immigrants actually do report taxes. In which case they use ITIN, which is compatible to SSN format-wise (###-##-####)... but it is _not_ unique over time.

</comment></comment></comment></comment></comment></comment></comment></comment><comment><USER11> > Botnets will just keep guessing until they find one.

Why does your security rely on primary key obscurity? This seems like you're doing something horribly wrong, put some authentication on that or something.

And no, no they won't. Hitting a collision is very hard if you're using cryptographic strength random UUIDs, you wouldn't even be able to bruteforce 64 bits over the internet in a reasonable timeframe.

Go ahead, try the math on that, the only reason small keys are vulnerable to local attack is because you can perform an enormous number of attempts per second, often in thousands of millions of attempts per second and they can keep at it for as long as they want. The database server won't let you query anywhere near that fast. You will never get anything like that for network based attacks as you're limited by bandwidth, latency and of course, the other side who will notice if you even try to do this for any significant period of time and likely block your attempts or limit them greatly.

<comment><USER12> _> Why does your security rely on primary key obscurity? This seems like you're doing something horribly wrong, put some authentication on that or something._

You can't put authentication on everything. Some things are inherently vulnerable to guessing because you can't toss an access control on it (the thing _is_ the access control), which what the high bit-lengths and rate- limiting are for. I wouldn't do this with a customer's UUID because it's static, but sooner or later you have to rely on security by obscurity somewhere (for example, password reset tokens).

As a theoretical matter a UUID shouldn't be the sole distinguishing data for authorized actions, but in practice it's probably best not to expose them in case you have an errant insecure direct object reference (which approximately all modern applications do, somewhere or other).

I'm just nitpicking your use of the phrase, "security by obscurity" here. That terminology is levied as a criticism against a lot of modern security design but it's really fine (and sometimes necessary) to use it for defense in depth (unlike, say, a cryptographic algorithm, which does not generally benefit from security by obscurity, though particulars of the peripheral infrastructure can).

<comment> vertex-four You can't put authentication on everything... but you can put an HMAC on almost everything, e.g. /receipt?id=1&hmac=hmacsha256(1, somesecretkey), which decouples your enumeration prevention from your ID system.

<comment><USER12> True, but you've just shifted the goalpost a little. Instead of brute-forcing a random token I'm now brute-forcing a MAC, or a secret key. You don't really need to use a MAC if you're already implementing rate-limiting and time-based expiry (in addition to use-based). That's just a performance hit. You might as well send a token generated by a good PRNG and skip the integrity checks.

It's unrealistic to brute-force one well designed token in the absence of authentication (if it's long enough you could go without any other security controls, including expiry or rate-limiting, but that's also a performance hit at scale). Adding one with an integrity check is effectively rate-limiting anyway.

Funny story though - I was once on an assessment in which a company used a scheme just like that. The UUID was HMAC'd for a password reset request, and UUIDs could be found by messaging another user. Unfortunately, they used the same secret key for password reset HMACs as the one embedded in their mobile app for HMAC signing requests. Worse still, password resets didn't need to be activated, so you could effectively takeover any account by retrieving the HMAC secret key from the app and running the victim's UUID through it.

<comment><USER13> The "performance hit" is the basis of most modern security [1].

[1] [https://en.wikipedia.org/wiki/Computational_hardness_assumpt...](https://en.wikipedia.org/wiki/Computational_hardness_assumption)

<comment><USER12> I'm well aware of the CHA (this is my primary research focus :). But that doesn't mean you should strive to make things computationally difficult if you don't need to (for example, Rijndael vs Serpent for AES). After a certain point there's no need for further computational cost.

</comment></comment></comment></comment></comment></comment><comment><USER14> This is why I'm starting to loathe SQL. The theory is great, but when the theory meets the practice and everything falls apart, the perfect kernel of relational beauty turns into a trash fire and I just want to get my freaking graph of objects out of the database. If I use numbers for keys, I deal with disaster when I try to merge from disparate sources. If I use guids as keys, I get terrible performance. Or I can just use a goddamned document store of Json or XML and have related objects get stored right next to their parents and tell the beautiful mathematics of relational algebra to shove it.

I'm tired of hearing "you don't have to say how to get the data, you have to tell the database what you want and it will get that in the most efficient manner" and then deal with an encyclopedia of byzantine rules to get it to do the aforementioned "efficient manner" with anything approaching decent performance. I can see the art, but the practicality mars it beyond recognition. It's like Venus de Milo sculpted out of duct-tape and bubble gum.

Sorry for the rant, I'm just getting frustrated with performance problems in _small_ data sets. I've taken the courses, I've read Date and Darwen, and I'm just starting to get terribly disillusioned.

<comment><USER15> I get this feeling from developers from time to time, and occasionally I'll get to see them on a project where they use a document store for long enough and they start to appreciate why we started using RDMSes in the first place.

You trade complex retrieval of subgraphs for complex retrieval of aggregate measures. Sometimes it is worth it, but I usually find that if you need both (and that is a big if) it's easy to scale relational databases than it is to scale document stores.

This is for many reasons, but an underappreciated one is that it is easier to cache individual objects than it is to cache aggregate measures. So if you need a subgraph just store it as JSON in a table full of simple key value mappings with some tables that handle how to invalidate the cache. But because aggregate measures span so many business objects it it's harder to achieve this. Furthermore, it is more likely to be _possible_ to enumerate subgraphs than it is to enumerate aggregations.

For example, imagine a tool that estimates the number of companies that meet certain criteria. "Count the companies by country that have at least one employee that earns at least $100k a year and who has had at least 2 previous positions." The number of permutations are endless, and while these types of queries aren't trivially fast on RDMSes, it is at least possible to handle them.

I'm not saying any of this is really easy, but humans don't live in an easy world. I used to be a structural engineer (EIT), and what amazed me was that despite the fact that we've built thousands of skyscrapers, each one was its own hell. Concrete trucks would go bad, water would gush out of places in the ground it wasn't supposed to, people would die. But we continue to push forward because we want to succeed.

</comment></comment><comment><USER16> I recall reading something about this in the PostgreSQL mailing list, message written in 2016 but may still be relevant

[https://www.postgresql.org/message- id/20151222124018.bee10b6...](https://www.postgresql.org/message- firstname.lastname@example.com)

"There's no substance to these claims. Chasing the links around we finally find this article: [http://www.sqlskills.com/blogs/kimberly/guids-as-primary- key...](http://www.sqlskills.com/blogs/kimberly/guids-as-primary-keys-andor- the-clustering-key/) which makes the reasonable argument that random primary keys can cause performance robbing fragmentation on clustered indexes.

But Postgres doesn't _have_ clustered indexes, so that article doesn't apply at all. The other authors appear to have missed this important point.

One could make the argument that the index itself becomming fragmented could cause some performance degredation, but I've yet to see any convincing evidence that index fragmentation produces any measurable performance issues (my own experiments have been inconclusive)."

<comment><USER17> MySQL's innodb has a clustered index. On DB2, SqlServer, Oracle, etc, they are configurable. PostgreSQL is more like MySQL myisam in that the data and index are separate. In this case, the random nature only screws up the index (assuming btree), not the data.

It can be a bigger issue if you proxy sql queries and shard to 1000 DB servers and you thus rely on part of the query to indicate which shard it is on. You can do this with a type of uuid that embeds the shard, or you have to include additional parts in the query beyond the PK to get the row (it could be a column that the sharing is based on, or a comment). And yes, careful with joins from an a root object's table that joins to other tables that use uuids unless you can guarantee that they are in the same node.

</comment></comment><comment><USER18> "Things got really bad in one company where they had decided to use Latin-1 character set. When we converted to UTF-8 several of the compound-key indexes were not big enough to contain the larger strings."

This shouldn't be right. UTF-8 encoding uses the same 8 bits for each valid UUID character that Latin-1 would. Unless someone put invalid characters in the UUID field, I would guess that the new encoding was actually UTF-16 or something.

<comment><USER19> In MySQL for example, using UTF-8 charset will cause it to use 3 or 4 bytes per character.

All the bytes transmitted on the wire will be UTF-8 strings, but this is disconnected from the underlying storage engine.

I don't know if this is the case for other databases, but when I read this my first thought was "This must have been MySQL"

<comment><USER20> W... What? Why?

The whole point of UTF-8 is that it's identical to Latin-1 (edit: ASCII) except in cases where it can't be. In those cases, it uses a very efficient encoding.

I understand that people (for whatever reasons) don't want to store text in UTF-8, but that's incredible that such a popular piece of software would take that notion to the extreme. "Let's make UTF-8 almost equivalent to UTF-32 when the data is at rest! Yes!"

Are you sure that info is correct? And if it's correct, are there any valid engineering reasons for that decision? It's probably best to be charitable and assume there must be a reason.

<comment><USER21> I think this is correct for CHAR and incorrect for VARCHAR.

A CHAR column always allocates the maximum size, so a utf8 (MySQL's weird "UTF-8, but only for the BMP" encoding) CHAR(100) needs to reserve 300 bytes per row, and a utf8mb4 (MySQL's actual UTF-8 encoding) CHAR(100) needs to reserve 400 bytes per row.

But a VARCHAR is dynamically allocated, so, yes, a VARCHAR(100) that stores 100 lower-ASCII characters is only going to use 102 bytes of storage (using 2 bytes for the string length).

See, e.g., the "tip" section at the end of [https://dev.mysql.com/doc/refman/5.5/en/charset-unicode- utf8...](https://dev.mysql.com/doc/refman/5.5/en/charset-unicode-utf8.html)

<comment><USER22> Author mentions compound keys, so issue was probably with the index key prefix length, which is by default limited at 767 bytes. That limits you to a maximum of having just first 255 characters of string data indexed with utf8, but in latin1 (one byte per character) you can have 767 characters long prefix. So with latin1 it's perfectly ok to have compound index on 2 varchar(255) columns, but you can't do that with utf8. Converting from latin1 to utf8 will break all those indices.

<comment><USER23> The 767 limit is with the default row format. In 5.7 the row format changes, and allows 3000+

</comment></comment></comment></comment></comment></comment><comment><USER24> sounds like the author thinks "uuids are a pain" and wants the benefits of them but with a smaller representation. but doesn't provide any reasonings why uuids are a pain other than not being able to remember them or say them out loud. these are not things anyone does with primary keys!

you'll never say this out loud : 7383929. you may be able to remember it, maybe. in a uuid you'll match the last few and first few letters just as fast in your head

uuids are fine. sorting is an issue but at scale (the entire point of this article) how often do you need to sort your entire space of objects by primary key? you'll have another column to sort on

hiding primary keys and having 2 keys seems like a great way to make all queries and debugging 2x as complicated

<comment><USER25> You'll never say a phone number like 738-3929 out loud? Short identifiers are useful in a lot of places. I work in IOT, and it's nice to have a short device id when we need to troubleshoot something.

<comment><USER26> Where I come from, our tables go towards a billion rows, so there's copying of keys regardless of whether it's uuid or sequential id.

</comment></comment></comment><comment><USER27> Maintaining UUIDs is much easier than maintaining id/int lookups that may be autonumbered (mssql, mysql, pg) or sequenced (oracle), even if using them internally and UUIDs externally. This especially comes into play when syncing across dev, staging and production environments and when clustering and servicing out parts of your app.

The moment any db starts to grow to these areas, UUIDs lead to far less issues than incrementing ids everytime.

Most RDBMS now have optimizations and native types (uniqueid) for UUIDs/GUIDs and this is really a moot point at this point, most UUIDs are no longer strings in DBs unless legacy from the time before native UUID types.

UUIDs are right for most projects but not all and as typical in any system, the environment and needs of your project will dictate whether it makes sense to use them.

UUIDs eliminating the round trip and negating dealing with autonumbering/sequencing is a massive benefit, the only real con of UUIDs is the extra 8 bytes but make up for it in less need to lookup during runtime when creating new or associating data with them.

<comment><USER28> It is like that line from jurassic park 2...

(The problem with UUID is that the two first letters are a cruel joke. But hey: people need to make their own mistakes because some things you can't just tell people: some rakes they need to step on until they learn or are unable to reproduce)

<comment><USER27> Universally Unique, if there is ever a conflict you can easily use a constraint or check upon creation on the app/server/service side, no different than checking if an int id or slug/public code is already used. No roundtrip is needed for every new item and if there ever was a conflict you just have an api error stating as much. You might want to also have it email you to play the lotto that week because it is virtually impossible.

Try syncing autonumbering across different database types where some use autonumbering and others use sequences. That is a nightmare scenario but there would be zero issues with UUID keys.

Primary keys as ints/longs will be constant rake stepping as you grow and maintain an app.

UUIDs emerged from a need that int keys weren't filling especially in a distributed/serviced world. I have used them in most DBs for over a decade and no rake steppage.

Side note off topic: If you were a piece of data would you want to be a universally unique piece of data or an int and merely a clone? All my data is a unique piece of data, I am a good Bender.

<comment><USER28> I've worked on quite a few distributed systems where you need to generate unique IDs and I can tell you that UUID is not a solution in quite a few of them because you actually need to understand what problem you are solving rather than taking the promises of others for granted or shrugging and gambling on thins sorting themselves out.

In many cases you have two problems: network partitions and/or high ID assignment rates. So statements "if there ever was a conflict you'd have an API stating as much" is simply passing the buck to some piece of magic and actively not knowing what goes on. It is a bullshit statement.

In my experience, this is the kind of problem where people tend to fail because they don't grasp that it is actually kinda hard. Especially if you have additional constraints (like the size of IDs). People tend to make all manner of assumptions and then not test those assumptions.

The arguments are familiar though. I hear a re-run of them about every 2 years, when someone has a system in production and the thing starts to generate duplicate IDs. Although both code and notes are available on previous solutions within the company, people tend to ignore that and just plow ahead.

</comment></comment></comment></comment><comment><USER29> Can confirm: using MySQL and for reasons... everything in the DB gets a primary key set by taking a random UUID, stripping the dashes, and then doing an `UNHEX(id)` in the stored procedures. Those IDs are both the primary keys and the keys used in the service's APIs.

One of our Ops guys did an experiment where they put a uniqueness constraint on the ID column and added an auto-incrementing primary key column that's never exposed to the code driving the thing. It apparently sped up our DB performance by orders of magnitude.

It also turns out that MySQL would perform faster just by leaving those values as strings instead of converting them to binary values. We've got some outside pressure to use Oracle instead of MySQL, and apparently it performs much better than MySQL with our current schema so we apparently aren't going to do anything to improve the MySQL performance or change any of this behaviour.

</comment><comment><USER30> Shameless plug: Anyone bothered about the wasted space in UUID string representation (and using Ruby) can check out [https://github.com/sudhirj/shortuuid](https://github.com/sudhirj/shortuuid) \- it re-encodes your UUID into any alphabet you choose, with a Base62 default (I find that to be a sweet spot that gives both URL safety and efficiency).

Let me know if you want ports in any other languages - the the algorithm is to really just treat the UUID as a hexadecimal number (that's actually what it is) and re-encode it into any other alphabet of choice.

That said, always use native UUID types in datastores - they'll convert to bytes / numbers internally and will always be the most efficient. For other situations, remember that they're just numbers, so you can write them in binary, ternary, octal, decimal, hexadecimal, vowels, baseXX or really any other alphabet you want. The bigger your alphabet (as long encoding remains efficient, like ASCII under UTF-8), the better your gains will be.

</comment><comment><USER31> Yep, glad to see this posted. In the python world, this is why we have UUID.int ([https://docs.python.org/3/library/uuid.html#uuid.UUID.int](https://docs.python.org/3/library/uuid.html#uuid.UUID.int)), though the native postgres UUID type with uuid-ossp works well too if you need them auto-generated in the DB rather than in application code.

<comment><USER32> Is there an auto-incrementing version of this? I'm trying to implement a time- series database and can't find a suitable method of generating incremental UUIDs.

<comment><USER33> You may try ULIDs - "Universally Unique Lexicographically Sortable Identifiers" posted here last year. [https://news.ycombinator.com/item?id=12205158](https://news.ycombinator.com/item?id=12205158)

</comment></comment></comment><comment><USER34> The reason I don't like the internal-int-external-UUID strategy is that _all_ of your queries now require an extra join. It's no longer "select microblog.* where userid = ?" now it's "select microblog.*,user.id from microblog,user where microblog.userid = user.id and user.uuid = ?".

This may be practical from a storage standpoint but string-based indexes on an SSD are pretty damned efficient.

<comment><USER35> The benefit of an external guid is that it obfuscates counts.

I've seen cases where it's possible for a business to track a competitor's growth by watching a public facing user id count increase.

<comment><USER36> I do that too.

Ask an invoice from a HN startup. See the reference number at the top "2017-0000438".

They had 438 potential customers this year :D

</comment></comment></comment><comment><USER37> This article is so poorly written it's hard to take it serious. The entire paragraph about the size of a UUID takes reading it three or four times before you can actually understand what the author means...

In what context would a primary key change, even when sharding? In my entire career I have yet to see it. Also any sane person would never sort random values. If you need sorting in your table, provide some kind of indexed timestamp.

<comment><USER38> > In what context would a primary key change, even when sharding?

The only time I've seen that happen is when a DBA evangelized for just using 'natural' keys, which in this case were slugs based on the item name. The problem came when users decided they wanted to rename things and then wondered why the IDs they were seeing in URLs didn't match the new names.

I'm not a believer in natural keys anymore.

> If you need sorting in your table, provide some kind of indexed timestamp.

A v4 UUID contains a timestamp. So it seems a bit wasteful to add the separate timestamp column. Why not just use the timestamp built into the ID? This is what MongoDB's internally-generated IDs do. (Though they're not quite UUIDs.)

There's a catch with v4 UUIDs though: the timestamp portion of the ID is not the most significant digits, so a simple ORDER BY doesn't do what you want.

I like the solution developed by Instagram, which Rob Conery discusses at the link below. It uses a pretty simple plpgsql function to generate a 64 bit integer based on the current time, a shard ID, and a per-shard sequence. This lets you generate 1024 IDs per second per shard. It also puts the timestamp portion of the ID first, so if all the shards' data ends up in Hadoop or something you can order consistently across the whole set.

[http://rob.conery.io/2014/05/29/a-better-id-generator-for- po...](http://rob.conery.io/2014/05/29/a-better-id-generator-for-postgresql/)

<comment><USER39> Item name was never suitable as a key... It's not stable. There are natural keys, they are just very rare. And the thing is even when you think you've got one it is often better to err conservatively. A good anti-example is SSN, often used in text books. They do change. Other than DNA sequences I can't think of a good person key. Just use an internal surrogate and be done with it.

<comment><USER40> Even DNA changes!

[http://www.sciencemuseum.org.uk/whoami/findoutmore/yourgenes...](http://www.sciencemuseum.org.uk/whoami/findoutmore/yourgenes/wheredidyourgenescomefrom/whydopeoplevary/doesyourdnachange)

<comment><USER41> Not to mention all the identical twins out there.

</comment></comment></comment></comment></comment><comment><USER42> Lots of talk about performance, but no numbers cited. I did my own benchmarks before using sequential ("COMB") GUIDs as 'PRIMARY KEY' (yes, they're surrogate keys) and found no material performance difference. I didn't keep the results, but someone else has made their numbers public here: [https://blogs.msdn.microsoft.com/sqlserverfaq/2010/05/27/gui...](https://blogs.msdn.microsoft.com/sqlserverfaq/2010/05/27/guid- vs-int-debate/)

</comment><comment><USER43> Little rant on UUIDs:

Notice how the author assumes UUID v4[1] in the conversation. There are very few reasons to use the other versions but we are still paying for their price in code complexity all the time.

Look at this UUID parsing code: [https://github.com/sporkmonger/uuidtools/blob/master/lib/uui...](https://github.com/sporkmonger/uuidtools/blob/master/lib/uuidtools.rb#L154-L172)

What it really should be is `[uuid_string.gsub('-', '')].pack('H*')` (for non- rubyists: remove the dashes, decode the hex back to binary).

Their representation is also not that good since hex encoding is not very compact.

I guess what I'm trying to say is that UUIDs are often used as a default unique identifiers but they are actually not that good.

[1]: [https://en.wikipedia.org/wiki/Universally_unique_identifier#...](https://en.wikipedia.org/wiki/Universally_unique_identifier#Version_4_.28random.29)

</comment><comment><USER44> Postgresql has a UUID type which should store them as a 16-byte number. If you use time-based UUIDs – for instance based on the unix time stamp in hex, like CouchDB – then you also get sortable primary keys, which conceptually might or might not be useful to your application, but it probably speeds up indexes. I've done exactly this for two different projects, and it works well.

On top of that you get IDs that are impractical to guess, which while wouldn't replace other security measures, would still give you some collision resistance and probably avoid some bugs because of the unlikeliness of accidentally picking the same key for two different entities.

I'm sure there are pathological cases for UUIDs as primary keys in certain scenarios, like perhaps a very high number of small records, but I've not come across them myself. You obviously have to know your own data and database if you have some very specific requirements.

</comment><comment><USER4> I work on an Entity Framework Core Implementation for MySQL and we recently added sequential GUID generation for primary keys that are of type Guid. The first 8 bytes of the GUID are the current UTC timestamp in ticks and the last 8 bytes are cryptographically random.

One interesting thing we ran into when implementing is that C#'s binary format and string format must be different to be sequential. So we have to detect whether the GUID is stored as a string or binary and put the timestamp in the correct place to ensure it is actually sequential.

Here's the PR for the feature for anyone interested: [https://github.com/PomeloFoundation/Pomelo.EntityFrameworkCo...](https://github.com/PomeloFoundation/Pomelo.EntityFrameworkCore.MySql/pull/260/files)

</comment><comment><USER45> Datomic [0] uses SQUUIDs [1] (Semi sequential UUIDs) to work around this:

        Many UUID generators produce data that is particularly
        difficult to index, which can cause performance issues
        creating indexes. To address this, Datomic includes a
        semi-sequential UUID generator, Peer.squuid. Squuids are
        valid UUIDs, but unlike purely random UUIDs, they
        include both a random component and a time component.

[0] [http://www.datomic.com/](http://www.datomic.com/)

[1] [http://docs.datomic.com/identity.html#sec-6](http://docs.datomic.com/identity.html#sec-6)

edit: formatting

</comment><comment><USER46> > Aside from the 9x cost in size, strings don't sort as fast as numbers > because they rely on collation rules.

Why would you sort these to begin with; what ordering of essentially randomness (part of the point) makes sense?

<comment><USER47> for when everything else is equal, you need something to let you get consistent sorting in your output

For example, if your page size is N and you have N+1 things with the same timestamp, you need a backup sort key or your pagination breaks down

<comment><USER48> You should really read this: [https://www.slideshare.net/MarkusWinand/p2d2-pagination- done...](https://www.slideshare.net/MarkusWinand/p2d2-pagination-done-the- postgresql-way)

<comment><USER49> Yep the Seek Method for pagination as suggested there would cause you to be sorting by UUID.

        SELECT uuid FROM t where filterable_field > $whatever and uuid > $lastSeen limit $pageSize

with a covering index on (filterable_field, uuid)

</comment></comment></comment></comment><comment><USER50> "UUIDs do not reveal information about your data" \- this is false statement; in sensitive environments you need to be aware that some UUID versions can leak MAC addresses, timestamps, hashes of your data etc. - sometimes just enough to abuse this information.

<comment><USER51> Yes, UUIDs are guaranteed to be unique, not random. You shouldn't use them for things like Single Sign-on tokens.

That being said, the author does talk about not exposing the UUIDs either. I get the feeling it's slightly less bad if a UUID gets exposed accidentally than an increment sequence number.

<comment><USER50> UUIDv4 is random. UUIDv3 and v5 are just namespace+name hashes (where namespace is often Nil UUID adding to the problem). Those 3 hashes are, in general, not good candidates for pk on large resources because they mess up the index which likes monotonic increments on new values.

The bottom line is UUIDs are not bad, you just need to be aware of couple of things when working with them.

</comment></comment></comment><comment><USER52> Why should it matter if you can guess IDs? Presumably records are locked in such a way that simply knowing a URL doesn't allow you to bypass security.

<comment><USER53> >Presumably records are locked in such a way that simply knowing a URL doesn't allow you to bypass security. In case this doesn't happen. If there is a leak, with sequential ID, you run the risk of leaking the whole table. With UUID, the leak can be more limited.

Also, sequential ID may reveal some information you may not wan to share. If someone's user ID is 1234, that may be an indication the service has at least 1234 users.

<comment><USER54> Timing based UUIDs (type 1) or ULIDs can leak (surprise!) timing information. If you can see them then you can infer the rate at which some resource is being created. For example the rate at which a service is creating users or business transactions. This can be valuable competitive information you don't want to share with the world.

<comment><USER52> I was responding to the article saying integers as IDs were bad because you could increment them to guess at the next record.

</comment></comment></comment></comment><comment><USER55> The strategy "internal int-external uuid" can be simplified if you use encryption and hypermedia API. It's possible to encrypt int and some additional information and format it as uuid v4 (random). For external users that know natural keys of some objects it's possible then to discover the rest of objects by navigation via API, where UUIDs are just some pseudo-random parts of the URIs.

<comment><USER56> Why would you format it as a UUIDv4 instead of just leaving all bits intact?

<comment><USER33> To take advantage of native UUID support in a database, e.g. PostgreSQL stores them in 128 bits. Also every UUID carries version number so minimal formating needs to be performed to comply with the standard.

<comment><USER56> So you're saying Postgres actually wastes cycles verifying the version information and doesn't treat UUID just as a 16 byte opaque structure?

<comment><USER57> Well, there's the "input format" (or rather two), which gets parsed into the internal representation. The input format can be either hex text, which allows dashes in some positions, or binary, in which it's just a bytearray. There's no verification otherwise, unless you treat generation of UUIDs, which allows to specify the type, as such.

</comment></comment></comment></comment></comment><comment><USER58> PostgreSql has a dedicated UUID column type. Those are fast and the storage difference is insignificant.

</comment><comment><USER59> One huge benefit of UUID is how you can safely create them while being offline, and then sync them at a later stage without conflicts.

<comment><USER60> You _can_ still have conflicts though, sometimes more than you think due to the UUID generator used. It's rare but it seems like an edge case that should be handled.

Doesn't invalidate your point about being able to make them offline though. Another option I considered to create PKs offline was a userid/timestamp pair.

<comment><USER61> Obviously you should be using UUIDv4 with a good secure pseudorandom number generator if you want to generate them offline. And typically no one handles collisions in large pseudorandom values.

</comment></comment></comment><comment><USER62> I don't agree with many points.

1\. Store uuids in a uuid field. Why starting the article with such a trivial finding that a text field is not optimal.

2\. Use sequential uuids.

3\. Several benchmarks have shown that the performace hit is minimal.

4\. The only way to communicate with ids is to copy and paste them. Never try to memorize, talk about them or type them.

</comment><comment><USER63> Excellent post, write ups like this are the reason I keep coming here.

What about the hi/lo algorithm as a middle ground?

[https://vladmihalcea.com/2014/06/23/the-hilo- algorithm/](https://vladmihalcea.com/2014/06/23/the-hilo-algorithm/)

In short, and I hope I don't oversimplify, each "shard" or "cluster" in the database gets a "block" of ids it can then go and assign on their own, the sequential "atomic" increase happens only once per hi "block", lowering the contention.

This gives you nice integers, incremental-ish most of the time.

I like the notion of integers internally and UIID (as integers of course! I would have never saved one as a varchar, I swear! ok, I was a noob... I deserve to be shamed)

Great post all in all!

</comment><comment><USER64> "Then add a column populated with a UUID (perhaps as a trigger on insert). Within the scope of the database itself, relationships can be managed using the real PKs and FKs." That would mean doing lookups by UUID, which is /really/ bad for performance. UUIDs are evenly distributed, so index caches are rendered nearly useless. With sequential keys, and access patterns that touch mostly new data, all you need to find the row is likely to already be in RAM, no matter how many rows you have. With UUIDs, you'd end up doing random I/O. Might not sound like that big deal to some, but we got a 3x overall throughput increase in one of our apps by switching from UUIDs to sequential ints.

<comment><USER65> I've found many have made the reverse argument, I assume for write-heavy loads. With incoming new UUIDs likely being on different pages, any locking within the DB is likely to be uncontended with a UUID key. Most people's RDBMS use cases are read heavy anyway, though, so it's usually better to have sequential (clustered) keys.

<comment><USER66> I've heard that case made for certain write-heavy workloads, too, but I would expect that to be highly dependent on the details of the db engine's implementation and your particular workload. While UUIDs, as you said, would generally be written to different pages on insert, you'd also experience a higher page split rate when UUIDs arrive that need to be written to pages that are already full which could be expensive. Without knowing the details of your db engine and/or workload really well, I'd expect you'd have to do some testing of your workload to see which one would come out ahead. In a series of tests that I did on application with a 95% write, 5% read workload, integer IDs were substantially higher performing than GUID IDs.

</comment></comment></comment><comment><USER67> Another alternative to avoid guessing is to use randomized 64-bit integer keys. You still risk collisions over sharding/replication, but only if you truly have a lot of data. You potentially lose some index performance but it shouldn't be any worse than with guids. If you really need the full size of a guid, just use them for the key. I don't get the rest of his argument for hiding internal surrogate keys.

<comment><USER36> I am sorry but that is bad advice.

A GUID has much superior collision resistance than a bare random integer.

The GUID includes a part that is a random (like the int), a time component (prevents collision for hashes generate at different microsecond) and network information (prevent collision across host names).

</comment></comment><comment><USER68> In a time-series datastore, you may have to replace a set of invalid/corrupt events within an index. Having IDs that are in some way deterministic from the source data, you are able to replace the invalid documents by ID by simply re- indexing that time period with your patch applied. This is the most simple and least risky solution, with minimal downtime

If the IDs are UUID, then the easiest way to fix the values is to drop the index and re-create it, making all of the other data in the index unavailable as it's being recreated.

The less-easy way with UUIDs is to select just the broken events, create new patched events, delete the old events, and insert the new ones in the right index. But you'd have to branch off of your regular indexing logic to do this, probably writing a separate script. Of course if you make a mistake, you may end up with either duplicate documents or loss of data, compounding the original problem.

So I agree, have IDs that are deterministic (that they can be recreated using some known formula and source data, for example: documenttype_externalid_timestamp).

</comment><comment><USER69> Using UUID for external means you've just forced all the problems with UUIDs on your users.

I'm dealing with that from several vendors atm.

</comment><comment><USER70> > _A naive use of a UUID, which might look like > 70E2E8DE-500E-4630-B3CB-166131D35C21, would be to treat as a string, e.g. > varchar(36) — don't do that!! "Oh, pshaw", you say, "no one would ever do > such a thing."_

> _Think twice — in two cases of very large databases I have inherited at > relatively large companies, this was exactly the implementation. Aside from > the 9x cost in size, strings don't sort as fast as numbers because they rely > on collation rules._

Eh, I've done that before because it made some interaction with Entity Framework easier (don't recall what now). Hasn't really mattered. The space for storing GUIDs has never been a meaningful constraint for anything I've ever worked on (9x is also nuts and assumes that your database uses 4 bytes per character). Sorting UUIDs is also generally uninteresting since they aren't meaningful by themselves. Maybe if you're doing lots of joins you might care about this.

<comment><USER71> It bothers me a bit why the author never mentioned what is the right data type to use instead of varchar(36).

I had to google it up to know there is a data type for GUID called "uniqueidentifier" (MS SQL) or "uuid" (PostgreSQL). As for MySQL, there is no such equivalent and suggestion is to use CHAR(38) instead.

Does that mean MySQL will have to live with the 9x cost in size issue?

<comment><USER70> binary(16) or two 64-bit integers are also options.

<comment><USER71> Thanks. Good to know that too.

</comment></comment></comment></comment><comment><USER72> This is just opinion and looks like UUID is bad for a particular case author is working on.

We have multiple components over different stacks and id could be generated anywhere in the components. We had to live with either building unique id per table separate infrastructure or UUID. UUID works perfectly and with POSTGreSQL, it's just awesome.

</comment><comment><USER73> I found this post super helpful. For anyone doing rails development, I wrote a gem to make this really simple. I'd love feedback.

[https://github.com/cyrusstoller/public_primary_key](https://github.com/cyrusstoller/public_primary_key)

</comment><comment><USER74> > Another problem is fragmentation — because UUIDs are random

UUID-4, UUID-3 and UUID-5 are random (3 and 5 are hashes).

UUID-1 is time-based with the time leading, and you can often control the _sequence_ (14 bits) and _nodeid_ (48 bits) fields to be used as whatever you want to avoid collisions.

<comment><USER75> With UUIDv1, the low bits of the time come _before_ the high bits, so they still don't sort in time order.

<comment><USER76> That depends on how your database sorts UUIDs. SQL server, for example, doesn't sort them in the obvious way ([https://blogs.msdn.microsoft.com/sqlprogrammability/2006/11/...](https://blogs.msdn.microsoft.com/sqlprogrammability/2006/11/06/how- are-guids-compared-in-sql-server-2005/))

It even is different from how C# orders _System.GUID_ instances. I think that could be something that can be put to good use if there ever is an _underhanded C# contest_ mimicking the underhanded C contest ([http://www.underhanded-c.org/](http://www.underhanded-c.org/))

</comment></comment></comment><comment><USER77> At a previous company, we got burned using UUIDs as MySQL PKs. Turns out MySQL keeps data on disk sorted in PK order, so even a moderate INSERT workload would lead to lots IO and disk thrashing as pages kept needing to be rewritten.

Fun times...

</comment><comment><USER78> My advice (and daily practice): If IDs are exposed, expose them as strings. If that ID is a compound key of a database, serialize it into a single string. If the ID is exposed via webservice, use URIs. In a entity provided by a webservice (e.g. a JSON-LD document via HTTP), use URLs or URNs. If possible, provide both and a translation service that translates URNs to URLs. URNs should be used for long term storage, URLs for transient use.

If I follow my advice, the type of an ID is an implementation detail of the persistence layer and/or service endpoint.

</comment><comment><USER79> I use the internal int and external uuid strategy mentioned at the end. It does make for somewhat confusing code for newcomers. I still don't love it.

</comment><comment><USER80> The reason given for not exposing UUIDs publicly (migration) doesn't apply to most NoSQL databases because they let you set the ID yourself so you can just copy each document as-is. Maybe the author was referring to databases which automatically (and forcefully) generate the ID on insertion... Even in this case, isn't there a way to tweak this temporarily just for the migration?

<comment><USER61> SQL databases of course allow you to generate the UUID on the client. (In fact not all database servers even _can_ generate UUIDs themselves. Postgres can, if you enable an extension.)

</comment></comment><comment><USER81> Use the hi/lo mechanism to generate IDs on the client. You can use a simple transaction to reliable reserve a range of numbers and then easily have incrementing numbers. Use longs and you can reserve a billion IDs per second and never run out.

This solves basically all the problems and we use it in production to number several tables with billions of events per day.

</comment><comment><USER82> I'm surprised the author calls out that knowing the pk before insertion is useful, but doesn't once mention idempotence as a key benefit.

If you are building mobile apps that sync state, UUIDs make your life so much easier. Optimistically perform writes locally, then perform writes remotely and retry on exponential backoff in case of a network error.

</comment><comment><USER83> When I was a developer on NHibernate, one of my favorite ID generators was something called HiLo.

Each of the clients reserve a chunk of Lo numbers, and increment the Hi number. Basically, they would pre-allocate a chunk of id ranges, and this allowed good distributed id allocation performance, while somewhat keeping local ordering.

Client generated ids are very useful to do.

<comment><USER84> You were a developer on NHibernate? Thank you very much, it was a pleasure using NHibernate (many years ago).

It was/is a very good ORM! And I used it extensively.

<comment><USER83> It was the best years of my career. I learned so much more than i did through my formal CS education, I was still a student at the time.

</comment></comment></comment><comment><USER85> for ruby on rails, acts_as_having_string_id [0] is a nice gem for not exposing sequential int primary keys:

it's nicer than using UUIDs because the strings are much shorter.

[0]: [https://github.com/hult/acts_as_having_string_id](https://github.com/hult/acts_as_having_string_id)

</comment><comment><USER86> I was at a new client the other day and notice that for all their tables in SQL SERVER, they use an IDENTITY column for primary keys, obviously seeded by SQL SERVER. What I found strange is that they allow deletes of records, allowing gaps in the sequence.

Is that normal practice? Their DBA was insisting that its normal.

<comment><USER87> No, it's not.

Also- wrt SQL Server ignore anything they say about GUID's as indexes. Just don't cluster them.

Also- enjoy the benefit of not having to ever ask the DB what the pk of those records you want to insert is going to be.

</comment></comment><comment><USER88> Using a better encoding than hex for the GUID would fix many of the storage and memory issues he cites: [http://eager.io/blog/how-long-does-an-id-need-to- be/](http://eager.io/blog/how-long-does-an-id-need-to-be/)

</comment><comment><USER89> This is what hash keys are great for. After getting the hash, convert to a BIGINT. Works great for me. You still get everything you do with UUID, but as a bigint so the numbers are much quicker, and its 8b.

</comment><comment><USER90> In my current project, in ms sql server, I have guid PK with unclustered index and clustered index on another field filled with current time stamp on insert.

What do you think about such setup?

</comment><comment><USER91> I've yet to see anyone mention storing UUIDs in a BINARY(16) column. Use exactly 128 bits to store 128 bits. We'd still have the random sort problem though.

<comment><USER61> Use a 'uuid' column if your DBMS was designed by people who knew what they're doing :-)

</comment></comment><comment><USER92> Shouldn't the title be appended with (2015)?

</comment><comment><USER93> He missed one of the biggest issues, in most implementations they are slow to generate due to the complexity and requiring a PRNG.

</comment><comment><USER94> > _Best of Both: Integers Internal, UUIDs External_

Database coder reinvents interned atoms.

</comment><comment><USER95> We use unixtime + server number + random as pk.

Works fine.

(10 million new rows everyday)

</comment><comment><USER96> How big a deal is this on Postgres?

<comment><USER97> Not at all. There's a native Postgres UUID column type, stored as a 128-bit number as opposed to a string. Postgres indexes are also not clustered, so will not suffer like MySQL when using an UUID as a PK.

Using an UUID is not performance optimal, but it is developer optimal and mitigates many other problems listed in this thread (offline sync scenarios, merging partitions or datasets, tracking across systems, opaque external representation perfect for APIs etc.) so unless you're really squeezing the last performance drops from a system I would simply use an UUID nowadays.

</comment></comment><comment><USER98> Yes, internal-use integer PKs are a really good idea, but difficult to scale to user-facing sites securely.

Often though, you need an automatic, user-friendly unique id for PK's to give users besides asking the user or generating a text slug.

Here's one construction:

friendly id = HMAC-SHA256(plaintext = "#{table-object-or-model}#{primary- key}", nonce = app-wide-unique-secret-token)

Then in the server app, keep a limited MRU cache (Hash table) of digests back to PKs.

Out-of-scope: authentication, authorization, CSRF/XSS and replay attacks.

</comment><comment><USER99> >The original issue with simple auto-incrementing values is that they are easily guessable as I noted above.

I don't think this is a real problem. If you're relying on your ID's being "unguessable" (and introducing engineering complexity to that end) for security you've already failed.

<comment><USER100> Auto-incrementing IDs also leak information. For example, if a service uses an auto-incrementing customer_id column and I'm assigned customer_id 12345, I know there are a maximum of 12344 other customers (making a few assumptions). This information is potentially useful for competitors.

<comment><USER101> what if I start at a random int number? consider your first customer to be 546472?

<comment><USER100> Sure, not a bad idea :)

That being said, I can still extract information. For example, I can create a user today, and then another user a week from today. From that information I can extract how many new users a service adds in a week.

To avoid that, I can increment by a value other than one, but that might make sharding harder unless I pick a number like 10, 1000, etc. (of course depending on sharding strategy), which is probably easy to figure out.

I guess my point is that it's harder to avoid leaking information with auto incrementing IDs. This may or may not matter depending on the use case.

</comment></comment></comment></comment><comment><USER102> This article sort of assumes you're using a relational database.

Most of the drawbacks discussed don't exist if you're using a key value store.

<comment><USER103> Right, and none of the drawbacks are relevant to toaster-oven or parachute use.</comment></comment>


Ask HN: Can Let's Encrypt be used for intranet/internal domains? - <AUTHOR> Let's Encrypt verifies domain ownership by placing a challenge response file on the docroot. Is there a way to issue certificates for non-public domains, such as router.company1.lan, router.company2.lan and such? <comment><USER2> No, if you don't control the domain according to public view (global DNS) you rightfully can't get certificates. Right now you also need to have a publicly reachable server behind it, but it's planned to allow DNS-only verification soon.

[https://community.letsencrypt.org/t/on-the-state-of-the- dns-...](https://community.letsencrypt.org/t/on-the-state-of-the- dns-01-challenge/4805)

<comment><AUTHOR> Thanks. I'll look at that.</comment></comment>


Amazon Plans Next Conquest - <AUTHOR> http://www.nytimes.com/2012/05/08/business/amazon-plans-its-next-conquest-your-closet.html?hp&pagewanted=print

<comment><USER2> Does anyone else find the article title grating? How does one leap into the high end of a pool?

Product videos are a pretty significant upgrade, though.</comment>


Apple Readies 'Apple One' Subscription Bundles to Boost Services - <AUTHOR> https://www.bloomberg.com/news/articles/2020-08-13/apple-readies-apple-one-subscription-bundles-to-boost-services <comment><AUTHOR> Mirror: [http://archive.md/QKlG9](http://archive.md/QKlG9)</comment>


Twitter's head of diversity is leaving and its chief HR officer has already left - <AUTHOR> https://techcrunch.com/2017/02/07/twitters-head-of-diversity-is-leaving-and-its-chief-hr-officer-has-already-left/ <comment><USER2> If your company has a head of diversity, your company is bloated and this person isn't going to fix diversity. I view these strictly as PR positions because I have yet to see anything useful come out of them.

<comment><USER3> I've yet to see anything come out of the PR-driven "diversity" push anyway. Call me back when those above the age of 55 can get jobs in Silicon Valley.

<comment><USER4> The fact you picked 55 instead of 30 says changes have already occurred in the right direction. Hopefully it continues.

<comment><USER5> That's likely just their age. Doesn't make it evidence of any change.

</comment></comment></comment></comment><comment><USER6> I'm sure a lot of the departures are just those leaving the sinking Twitter ship, but this person leaving so soon doesn't bode well for either party.

Diversity-as-an-initiative once a company has grown to a large size is seemingly a futile exercise as evidenced by these diversity reports, year after year.

If companies are actually serious about diversity, they need to make it part of the company's culture from the beginning, before all the typical tech hiring inertia takes place.

<comment><USER7> Late change is better than no change at all.

<comment><USER8> Actually this pandering to diversity fanatics is a pointless endeavour -- all this ingratiating just wastes people's time while normalizing racism.

We used to teach kids to be colorblind but now we're teaching them to see nothing but color. It's a sad state of affairs.

<comment><USER9> In an ideal world you would be correct and I hope nobody would disagree with your ideal desire (well, I know some do).

The reality, however, is still far from there. The point of diversity programs is not to accentuate the differences (although it can have that as a second order side effect), but simply to increase the rate at which these barriers break down. There are plenty of studies that show that people who are around others who are "different" (whatever the dimension: body shape/color, religion, political views, etc) in general stop being bothered by the differences. So it's in everybody's interest to try to get more of that. And in the long term it's good for business, even if it may or may not be in the short term -- but why should't corporations invest in the long term?

<comment><USER8> It's not in everybody's best interests to change some people's minds and "steer society" in a different direction by breaking down barriers. The blowback from trying to make people stop being bothered by differences is real and there are countless examples of it around the world and throughout history.

"Live and let live" is more than just a banal platitude, it's a rule that's useful in keeping society functioning. Throwing radically different folks together and telling them to accept each-other is a fool's errand.

<comment><USER9> > Throwing radically different folks together and telling them to accept each- > other is a fool's errand.

Fortunately the facts are against you. Whether it's Boston bussing (I lived through it) or the unremarkableness of women in business, military or government, or removing restrictions on marriage, acceptance demonstrably comes via contact.

There's still a long way to go but things have improved a lot. Just for me: I was born in a country with statutory restrictions on people of different "races". When I came to the US for the first time my parents drove north from Washington DC because had we looked for a hotel in VA my parents would have had to stay in different ones. That's inconceivable now.

And frankly we all have better things (from wifi to medicines to books plays and movies) because more people than before have the opportunity to participate. Shoving some of them down hurts everyone.

</comment></comment></comment></comment></comment></comment><comment><USER10> Wait, there's actually a position called "Head of Diversity"?

<comment><USER11> Well discrimination is illegal, and the the Department of Labor will sue you if your numbers are bad like Google's. [https://www.fastcompany.com/3066914/innovation- agents/google...](https://www.fastcompany.com/3066914/innovation- agents/google-and-tech-struggle-to-hack-bias-and-diversity) Google spent a quarter-Billion dollars trying to improve diversity for apparently no gains. It's an important thing to get right, and after a company grows to a certain point it takes a lot of effort to change.

<comment><USER12> Honest not antagonistic question, why is it important to get right?

<comment><USER13> It's both more effective and a remediation of past discrimination. Bear in mind this is only an obligation insofar as companies wit to contract with governmental entities.

<comment><USER8> Remedying past discrimination is not any company's responsibility, not by a long shot.

<comment><USER13> Have you tried reading comments all the way to the end before replying? I wrote two sentences in the post above, and the second one addressed that very objection.

</comment></comment></comment></comment></comment></comment><comment><USER14> I think I would thoroughly enjoy job shadowing this person. I'm very curious as to what their day to day activities would be!

<comment><USER15> Probably reaching out to minority college programs, graduates, and professional organizations. If your black, woman engineer, for example, you aren't necessarily reaching out to your old boys network for prospective jobs.

I'm assume you assume they sit in their chair all day and spin around in circles.

<comment><USER14> Harsh. No. I just had no idea and would be honestly curious. Just as I would be of any other job I never knew existed!

<comment><USER15> My apologies. I thought you were being facetious. Loads of posters on HN are when if it doesn't conform to their own world view.

</comment></comment></comment></comment><comment><USER16> Twitter engineer here.

Our engineering culture is actually pretty great - diversity is something we strive for and have made great improvements at, but definitely not at the expense of hiring talented people.

Check out this article published yesterday from a relatively new manager to hear what it's like on the inside: [https://medium.com/@kathleencodes/the- surprising-things-ive-...](https://medium.com/@kathleencodes/the-surprising- things-ive-learned-about-twitter-engineering-culture-925929113be4#.ofg77x4hd)

</comment><comment><USER17> I've sat in interviews for most positions (at where I work, anyway). The only one that requires actually knowing who the person is (gender wise, race wise, etc) is sales. Even in that case, it's only because of preexisting prejudice.

These companies will figure out sooner or later that anonymous interviewing is the only solution. Those who benefit from the status quo will protest, "rightly so". Those who don't won't be around to complain, and so a kind of survivorship bias persists.

<comment><USER18> I think you give up way more with anonymous hiring than you would ever get in return. How do you gauge verbal and non-verbal communication skills? Do they mesh well with the team. Once you drop the in-person interaction you lose more than just the ability to discriminate.

<comment><USER19> Two stage interviewing? A majority of the interview being anonymous, and then a portion being non-anonymous.

With that you can measure the effects of whatever attribute tag you want depending on the interview type.

Ex:

1\. Anonymous Interview

2\. Feedback Given

3\. 'Culture' Interview

4\. Feedback Given

5\. Attributes Recorded & Decision Made

<comment><USER18> I don't have any issue with this process beyond the use of 'majority'...Maybe I'm just skeptical, but I don't see how the majority of any interview could be non-interactive... Now maybe you think it can be anonymous and interactive but that's rarely true in the way you would want it to be. If it's chat we might over-focus on whether their writing is English as a second language, same for voice. Anything less than that is non-interactive, anything more than that is not anonymous in any way.

</comment></comment></comment></comment><comment><USER20> Probably because both the head of diversity and HR rep know how toxic twitter is and have given up on any kind of reform. Any person thats a minority and basically not a white guy has to run the gamut when it comes to twitter. The comments here are a testament to that.

Calling a company with a head of diversity bloated for instance is telling of your politics.

<comment><USER10> > Calling a company with a head of diversity bloated for instance is telling > of your politics.

So is it offensive to subscribe to MLK's general idea of judging people not on the color of their skin, but on the content of their character?

<comment><USER21> No offense but MLK did not agree with you, would not agree with you, and taking some words that are easy to white-wash out of the context of his true political philosophy is insulting.

[1] [http://www.nola.com/opinions/index.ssf/2014/04/martin_luther...](http://www.nola.com/opinions/index.ssf/2014/04/martin_luther_king_jr_explicit.html)

<comment><USER8> The spirit of MLK's teachings is that we should look beyond color, not remedy discrimination of the past by tilting the scales in favor of colored people.

<comment><USER22> > The spirit of MLK's teachings is that we should look beyond color, not > remedy discrimination of the past by tilting the scales in favor of colored > people.

Actually, tilting the scales in favor of previously-discriminated-against groups (including, but not limited to, "colored people") until such time as the resulting disadvantage from that discrimination was remedied is exactly and concretely what King advocated, for example, when calling, in _Why We Can 't Wait_, for a "Bill of Rights for the Disadvantaged" parallel to the "GI Bill" (then known as the "GI Bill of Rights") for veterans.

The right wing has taken to presenting, on the basis of a single line in the "I have a dream" speech, an utter fabrication about King's philosophy that conflicts with what King himself wrote and said on the specific issues for which they deployy the fabrication as an argument.

</comment></comment></comment></comment></comment><comment><USER23> Many of these comments seem to indicate that lots of people are threatened by diversity, that maybe their privilege is ending and they might start getting treated as equals.

And to those commenters who think current hiring practices are fair:

Are Emily and Greg More Employable than Lakisha and Jamal? A Field Experiment on Labor Market Discrimination

"We find little evidence that our results are driven by employers inferring something other than race, such as social class, from the names. These results suggest that racial discrimination is still a prominent feature of the labor market."

[http://www.nber.org/papers/w9873](http://www.nber.org/papers/w9873)

<comment><USER24> Almost everyone agrees with the idea of hiring based on merit instead of prejudice. But I very often see diversity advocates attacking the idea of merit-based hiring. For example, making fun of people that use the word "meritocracy" has become a popular meme. Personally I'm not sure how to account for this discrepancy.

<comment><USER13> Perhaps because 'meritocracy' is sometimes advanced as an excuse for ignoring other factors, without the proponent investing the effort to actually use double-blind method for selecting among applicants to avoid the possibility of bias among hiring managers.

</comment></comment></comment><comment> rm_-rf_slash Expecting employers to "fix" diversity imbalances is like rolling up to Anthony Bourdain in a garbage truck and telling him to do his best with dinner.

The problems with social inequality go waaay back. They go back to single- parent homes. They go back to childhood environments of violence and drug use. They go back to inequalities in access to food, water, shelter, K12 education, the internet, name it.

We Americans live in an outrageously unfair society that we are largely complicit in by not doing anything to change it beyond writing comments on the internet and wanting it to just happen already.

So if you leave it up to the employer to have a "diverse" workforce, diversity is already dead.</comment>


Senate Leadership Is Pushing Through a Dangerous Surveillance Bill As - <AUTHOR> https://www.commondreams.org/views/2020/03/16/senate-leadership-pushing-through-dangerous-surveillance-bill-americans-are-focused <comment><USER2> Actual subject is: Senate Leadership Is Pushing Through a Dangerous Surveillance Bill as Americans Are Focused on Covid-19

<comment><AUTHOR> Yes I know but there was a length limit so I shortened it.</comment></comment>


Hands-on with Vivaldi, the new Web browser for power users - <AUTHOR> http://arstechnica.com/information-technology/2015/03/hands-on-with-vivaldi-the-new-web-browser-for-power-users/ <comment><USER2> And just about the first thing you see when trying to install it…

 _Vivaldi may collect visitor statistics. The visitor statistics may include information about the visitors IP-addresses, usage patterns, <…>_

I guess I don't want your shiny new browser.

<comment><USER3> It's perfectly reasonable to expect extra data to be collected when using prerelease versions of software. Complaining about it now is premature.

<comment><USER4> That's not a tradeoff I'm comfortable with, "even" in pre-release software.

I'm not accusing you of this, but in other cases I've seen proponents of something say "This isn't a big deal, it's just starting out". Then, when people continue to complain a year from now, the defenders say "This isn't a big deal, it's always done this."

Vivaldi looks interesting, but I'd wager that there's a decent overlap between the Power Users they're after, and the group that values their privacy the most.

<comment><USER3> Privacy has nothing to do with it. If the software is not yet ready, users are expected to encounter serious bugs. Your QA department is paid to help provide you with information to diagnose and troubleshoot those bugs, but users aren't, so you need to collect that automatically.

</comment></comment></comment></comment><comment><USER5> This is the worst-written article I've read in a long time. It repeats itself (forgivable), contradicts itself (unforgivable) and I'm pretty sure is factually inaccurate in places (Doesn't Firefox already show the slowest addons?)

In spite of that Vivaldi looks interesting, and as a long-time fan of Opera (the original company and the browser) I wish them well.

<comment><USER6> The claims that there aren't new browsers is wrong as well - there's many of them, it's just that they don't have a marketing team behind them. Uzbl, luakit, surf, jumanji, midori, vimprobable, xombrero, etc.

<comment><USER7> Well, none of those are new. Breach.cc is relatively new but even that goes back a year. But these highly extensible vim-like microbrowsers you list (excl. midori) have been around for years.

</comment></comment></comment><comment><USER8> There are two things I really miss about Opera. Generic Content-blocking - that is not only blocking ads but any URL-pattern of interest. And also the per-site settings. For example a lot of sites use extensive javascript only for tracking, but not for functionality visible to the user, so blocking javascript on those sites truly improves loadtime on many sites. And also being able to fine tune cookie settings per site.

It's such simple functionality, but really makes a huge difference both for performance and privacy and it saddens me that Opera never will be what it once was, and probably no other browser either. :-(

<comment><USER9> Yeah, I want stuff like the F12 menu, and a good workflow for uses who _whitelist_ sites for cookies, javascript, and flash.

Now, Firefox might be able to fill the void, but I doubt Chrome ever can. Those kinds of features are an irritant to Google, because they want their pervasive analytics, ads, and various web-apps to work out of the box.

</comment></comment><comment><USER10> What I want from a browser:

1\. Speed 2\. Privacy 3\. Add-ons/features (and this is far below 1 and 2).

This browser collects all sorts of information (seemingly more than chrome based on the message from instillation), and consumes an absurd amount of memory per tab, even on plain text webpages.

There may be something promising there, but this isn't it.

<comment><USER11> This browser consumes about same amount as chrome in my tests. Maybe 1/5 more.

Firefox and Chrome also collect the same information, but it is an opt-in from what I remember.

Edit:

Chrome usage with 3 tabs: [http://i.imgur.com/mGiX0So.png](http://i.imgur.com/mGiX0So.png)

Vivaldi usage with the same tabs: [http://i.imgur.com/ejnNzib.png](http://i.imgur.com/ejnNzib.png)

<comment><USER12> > Firefox and Chrome also collect the same information

Uhm, reference? I seriously doubt that...

<comment><USER11> Well, first from [https://vivaldi.com/privacy/](https://vivaldi.com/privacy/) it's not clear to me whether the privacy thing applies to the browser or the vivaldi web site.

Second, here's google chrome's privacy policy:

[https://www.google.com/chrome/browser/privacy/](https://www.google.com/chrome/browser/privacy/)

You can see that they have all kinds of information being sent to google. At least as much as Vivaldi.

</comment></comment></comment></comment><comment><USER13> > And since Chrome arrived in 2008, the Web hasn't seen another major browser > launch—until now.

Lots of new browsers have launched since 2008, and Vivaldi certainly isn't a major browser yet, if it ever will be.

<comment><USER14> I normally respect the things you say on here, but for once, I do have to cry "Citation". If you would be so kind, can you list some of the _lots_ of new browsers that have been released?

<comment><USER13> Not an unfair questions. Another post already listed some:

[https://news.ycombinator.com/item?id=9159709](https://news.ycombinator.com/item?id=9159709)

Sure, these aren't major browsers -- neither is Vivaldi, yet, and its purely speculation that it might be in the future.

<comment><USER15> Furthermore, there is little reason to suspect Vivaldi might become major. It brings nothing substantially new on the tech side - it's using Blink, like Chrome and Opera, and like Opera, it only differentiates itself from Chrome on the UI side.

Many browsers have differentiated only on the UI side; none have really succeeded. As another example, Flock.

The 4 actually major browsers _have_ all brought major new innovations: Firefox was a different rendering engine than IE (and much better in pretty much every aspect, except legacy website support); Safari launched with WebKit (a new major fork of KHTML with many improvements); Chrome in turn launched using a major fork of WebKit (bringing process isolation and a completely new JS engine, v8).

Vivaldi brings nothing like that to the table. It's interesting to see new UI ideas, of course. But for the article to call it "the first new major browser in many years" makes me immediately wonder if the author doesn't own Vivaldi stock (which of course is not the case; but the point is that it seems unjustifiably positive on the product).

<comment><USER16> Firefox was an innovation compared to IE, but it was explicitly a trimmed-down version of Mozilla, which had existed in the form of Netscape going back what amount to geologic eras in terms of the history of the Web.

Firefox's innovation wasn't the rendering engine, per se, but everything surrounding it, including add-ons.

</comment></comment></comment></comment></comment><comment><USER17> As a loyal user of Opera 5-12 (and a grudging user of the new Opera, which I find marginally faster/less full of surprises than Chrome), I love that the article reads like a list of old Opera 12 features that I miss.

At the same time, I'm deeply worried that they believe they have to rebuild everything, including the mail client, to get to their first official release. Opera had a long pattern of rough but innovative releases that took a while to stabilize, and I can't help but believe that with them emphasizing feature completeness _first_ , it's going to be rough for quite some time.

Given the high bar set by modern browsers I'm not sure users--power users especially--will put up with the associated quirks long enough for Vivaldi to gain traction.

<comment><USER18> Still it proves that Opera 15 could have been done differently while still remaining Chromium-based.

</comment></comment><comment><USER19> Where's the source? What's the license? I couldn't find this information, and downloads seem to be binary-only.

<comment><USER20> From some quick searching, apparently it's closed-source, but built on top of Chromium's rendering engine (Blink).

<comment><USER21> Wow… why would anyone ever want to use this browser? Honestly. If it were open source, it would at least have a _chance_.

<comment><USER15> To be fair to Vivaldi, Chrome isn't open source either, and lots of people use it.

Chrome is of course mostly open source, except for some proprietary bits. The same is basically true of Vivaldi (and Opera), they all add a relatively small amount of proprietary code on top of the massive open source Chromium codebase.

<comment><USER22> AFAIK the only differences between Chromium and Chrome are:

* Preinstalled proprietary plugins (Widevine, Flash) * More preinstalled codecs (woohoo, patents!) * Various user metrics reporting

There is a pretty big difference between that and keeping all of your changes private.

<comment><USER15> Well, for Vivaldi and Opera, "all of your changes" in this case amounts to just the UI. Neither has the capability nor motivation to modify Blink in any significant way.

Also, Opera has been contributing to Blink, so they aren't keeping all their changes private.

Vivaldi hasn't launched yet, but I would guess the same would be true of them - it just isn't rational to do otherwise (if Vivaldi fixes some small bug in Blink, why keep it as a patch they need to constantly reapply on top of Blink? Far easier to upstream it).

So aside from the UI (which is tiny compared to the entire browser), Chrome, Blink and Vivaldi are about equal in terms of being open or closed.

<comment><USER23> But everything beyond Blink/Webkit is precisely what I'm interested in viewing, contributing to, and wanting the FLOSS community to be able to audit and alter, etc. etc.

I was a pretty die-hard Opera fan for many years, so this news really piqued my interest... up until I learned the software is not libre.

</comment></comment></comment></comment></comment></comment></comment><comment><USER24> As someone who tried using it: There's nothing about this for power users. Even with an absolutely minimal website, it consumes ~20mb per tab.

<comment><USER25> I imagine "power users" probably have more than 2GB of RAM.

<comment><USER6> But also much more programs competing for it - I have 6GB on my work laptop, and I'm constantly at 95%+ (not counting cache!).

<comment><USER26> This needs to be said - upgrade your RAM. If your device doesn't support a memory upgrade, get one that does.

Upgrading my laptop to 32GB RAM was one of those things where you wish you had done it a lot earlier. No more OOM-killers, no more things grinding to a halt because the box is swapping (in fact, I disabled swap altogether), no more checking memory utilisation before starting a beefy VM.

Just smooth sailing all along.

<comment><USER27> Yeah, until the next release of Chrome and Firefox, where GMail and Facebook together will start to happily eat 16GB of your 32. The amount of memory browsers now eat up is insane.

</comment></comment></comment></comment></comment><comment><USER28> It's worth noting that there is already an open source browser project to recreate + extend the Opera 12 UI: Otter Browser

[http://otter-browser.org/](http://otter-browser.org/)

</comment><comment><USER29> Let me know when Vivaldi implements vimperator style link selection and I'll try it. Otherwise if you have to click Firefox+vimperator or palemoon+pentadactyl will always be the true powerusers browser

<comment><USER30> Vim users are a niche within a niche. Not all developers use Vim or care.

<comment><USER27> Because not all developers are power users - but this browser is supposedly aimed at those of us who are.

<comment><USER31> Even as a vim devotee, I think that the suggestion that "power user" implies "vim user" is incorrect at best, and offensive at worst.

<comment><USER27> Of course. It implies "vim _or_ emacs users".

</comment></comment></comment></comment></comment><comment><USER32> Why can't they have stacked tabs and web page notes while using the Blink renderer? Saying that dropping Presto made those features go away doesn't make any sense to me. Its not like the Browser is built using its own renderer. The web renderer renders into the area of the tab ... the rest of the browser functionality exists outside that real-estate--and should be independent of the renderer.

</comment><comment><USER33> I loved and used Opera as my main browser back in the day, flicking back and forth between pages with right-mouse-button gestures and reading my email in- browser before Gmail. What a fantastic browser it was! I am excited to see the Vivaldi project mature and hope to be able to evaluate it once it is more complete.

</comment><comment><USER34> I gave Vivaldi a go because I wanted to speed up the testing of a website 'logged in' (with Chrome) and 'not logged in' (with Vivaldi). Vivaldi was notionally perfect for that with the same F12 tools as Chrome. I could delete session cookies and do my testing with it, no learning required.

So I would say I have given it a fair shot. But what browser am I using now? Chrome. Sometimes when you want to get something done you don't need to find that the maximise/minimise buttons are somewhere different, and with Ubuntu/Unity + Vivaldi they are - 90's style, in the top-right hand side.

I was actually a late convert to Chrome, for a long time I 'needed' Firebug and found the Chrome F12 tools to be inferior, or with a learning hurdle. With Vivaldi there is no leap of re-learning required, but, it doesn't have anything extra that I _need_ for the day job. A regular incognito window does all I need for testing, with the UX chrome in the expected places. Note I also have the indispensable 'Hover Zoom' and 'Vim bindings'.

<comment><USER35> I really, really, really want to like Firebug/Firefox but it's just impossible for me. I already tried to switch 2 times this year. Once with the FFDev and once with v36 (stable) - both times I was left just as disappointed. I open the developer tools and refresh a SharePoint page and the browser hangs for close to 1 minute - this is just unacceptable. Even IE doesn't have this hiccups. I know this isn't common and is only reproducible on v36 for me but it's a huge PIA. Other than this, I'm curious why do you say the chrome developer tools are inferior? I haven't used firebug in a while but AFAIK Chrome Dev Tools has most of it's features and since it's "native" it's smother and faster.

<comment><USER36> Try it while not logged into SharePoint. My experience when SharePoint is logged its not exactly lean.

</comment></comment></comment><comment><USER37> Why did they keep the built in mail client of all things? Fighting opera to get it to use my mail and RSS clients was the worst part of my experience with the browser.

If you want to make a customizable browser, give us the option to not install such unnecessary addons. The browser already seems to use as much memory as chrome, it does not need to waste any more.

<comment><USER38> This is something I don't really understand about the idea of a "power" browser. The entire reason browsers cut back on features was bloat that 99% of users never use. I guess targeting that 1% makes the bloat more reasonable, but for anyone who wants one feature and not another, it's easier to start at 0 with Chrome or Firefox and add the one thing you want.

<comment><USER39> On the other hand, the reason that Firefox got a reputation for being slow, bloated, and leaky was because 90% of the features were written by third-party developers with varying levels of competency, no quality controls, and little incentive to bring their stuff up to date.

I used Opera pre-12, and the reason I did so was because all of the features that I wanted were baked-in to the browser, which meant that they went through the same quality controls and integration testing as the rest of the browser. There was no separate step of updating add-ons, or even worse having outdated add-ons with no compatible version. Everything Just Worked.

Granted, some of the features were not as feature-complete as comparable Firefox add-ons. E.g., tab-stacking is strictly inferior to tree-style tabs because there is only one level of collapse. But everything I wanted was already included, at an acceptable-enough level that for me, it outweighed the cost of having to comparison-shop for mouse gestures or custom CSS solutions or whatever.

FWIW, Opera never felt "bloated" to me. It had crop-tons of features, but for whatever reason that didn't translate to a feeling of bloat. The browser was fast and responsive, and the pile of features never really got in my way. Every once in a while, I would read about an interesting Firefox add-on, dig into the Options menu, and find out Opera had the feature all along, whereupon I would drag it onto the UI.

Personally I like having the email built-in to the browser because it means I don't have to context-switch to see the status of my inbox. But that's a personal quirk.

</comment></comment></comment><comment><USER40> I was using Opera heavily until about 6-7 years ago when the compatibility just became too bad. The feature I miss most is the keyboard shortcuts (which this seems to have) and 'fast forward'. This was a neat feature for paged sites where the browser would try to parse the site for 'next page' links, preload it and when you hit the fast forward button it would show it immediately. Worked great in all kinds of user forums and photo albums back in the day - although granted, there's much less paged content nowadays. I used to put back, forward and fast forward on the 'z,', 'x' and 'c' keys, sort of similar to vim (although I was never a vim user). It really felt like being in total control of your browser and having the web at your fingertips.

</comment><comment><USER41> > even the ability to render a page with monospace fonts if you want.

Is there any major browser that _doesn 't_ allow you to do this?

<comment><USER42> In Opera you could quickly change these things for the current page in the menu. I don't think that is possible in Chrome or Firefox.

</comment></comment><comment><USER43> Not really my style (I'm a vim guy), but for power-user browsers, there's conkeror. Webkit rendering, emacs commandline.

[http://conkeror.org/](http://conkeror.org/)

(I mostly use it as a frequently-wiped-configs-and-cache bare rendering test.)

</comment><comment><USER44> I don't personally see whats so exciting about this, it really doesn't tick any of the boxes for anything I'd want in a browser (as a 'power user'), this just reminds me how much I dislike Javascript.

</comment><comment><USER45> I got this prompt when I opened HN in Vivaldi: [http://i.imgur.com/HKMLXQD.png](http://i.imgur.com/HKMLXQD.png)

When I click the ?, nothing happens. Any idea what this is?

<comment><USER46> My guess is that they want to use your password manager for Chrome to allow you to login to HN.

</comment></comment><comment><USER47> "The problem is bad enough that a future version of Firefox will even have a feature dedicated to letting you know which of your add-ons is slowing you down."

Does anyone know this feature of Firefox?

<comment><USER48> It's in Nightly. I've seen it a few times. It appears as a bar across the bottom of your screen telling you x addon may be causing this page to load slowly. From there you have the option to disable the addon, ignore the message, or ignore permanently. I saw it for uBlock, HTTPSEverywhere, and reddit enhancement suite, and amazonsmile redirect. In each scenario I felt it was well worth the tradeoff to run a little more slowly.

<comment><USER49> Worth noting that it's set to be hypersensitive right now for testing. Don't trust it for much of anything yet.

<comment><USER50> Good to know, I was starting to think either noscript/ublock were badly written, badly integrated or that Firefox had deep issues.

</comment></comment></comment></comment><comment><USER51> I installed it, but crashes every time I try to open it.

</comment><comment><USER52> It's not open source enough for me. No thanks.

</comment><comment><USER53> "first there was ie and Firefox"...

holy crap I am old ...cello anyone? :|

</comment><comment><USER54> Their website needs Javascript to load. Ugh. I hope that's not an indicator for the rest of their philosophy.

<comment><USER25> I don't understand why there are still people persisting in disabling JavaScript when the vast majority of Web sites today have significant functionality degradation or just plain won't work without it.

<comment><USER55> The vast majority of web sites are crap. I'd really rather default disable javascript and opt-in if I decide I want to allow their code to run on my machine.

<comment><USER27> This. It's not really about tracking. Most of the websites embed tons of JS for no good reason, just because developers are lazy fashionistas and designers wouldn't be able to justify their pay if they didn't add tons of unneccessary bells and whistles.

There are web _applications_ that need JavaScript, and I'm fine with that. There's lot of cool stuff JS could be used for, and it's not used - check out Bret Victor's explorable explanations. But instead doing something useful with this power, web designers and developers just use JS to make "shitty skeuomorphic bastardizations of what should be text communicating a fucking message" \- as the quote from a very insightful article goes[0].

[0] - [http://motherfuckingwebsite.com/](http://motherfuckingwebsite.com/)</comment></comment></comment></comment>