Если вы можете поместить это в английские термины, используйте assert для "gotta" (Got to, Must) и исключения для "otta" (Ought to, must).
Используйте assert для остановки показа, критических условий, которые должны быть истинными для продолжения выполнения. В качестве примера можно привести, что деление происходит правильно (подумайте об ошибке с плавающей запятой в микросхеме Intel) или что ваше соединение с базой данных не является нулевым после того, как вы правильно его открыли. Если это произошло, выполнение программы не должно продолжаться.
Используйте команду throw для предсказуемых ошибок, которые может обработать ваш метод. Бросок является частью контракта, который объявляет вам и другим программистам, что могут встречаться определенные типы ошибок (и что это не ваша ответственность).
В вашем примере я предполагаю, что нулевой потребитель или пустой список никогда не должны возникать при нормальных обстоятельствах. Если мои предположения верны, то вы можете использовать здесь assert, заявив, что subscribe () с этим справится.
Если мое предположение неверно и произойдет нулевой потребитель, скажем, 1 из 50 раз, бросок будет лучше, и вы заявите, что subscribe () формирует контракт с вызывающим методом, в результате чего вызывающий метод обрабатывает ошибка.