Faits : A. A.a. Le 17 février 2010, X._ SA (ci-après: la bailleresse) a convenu de céder à H.Z._ et F.Z._ (ci-après: les locataires) l'usage d'un appartement de cinq pièces au quatrième étage d'un immeuble situé dans la commune de ... (GE). Le contrat de bail a été conclu pour une durée initiale d'un an, du 1 er mars 2010 au 28 février 2011, avec clause de renouvellement tacite d'année en année. Le loyer a été fixé à 18'894 fr. par an, soit 1'574 fr. 50 par mois, charges non comprises. Le bâtiment abritant l'appartement loué était classé jusqu'au 31 décembre 2011 dans la catégorie des habitations à loyers modérés (HLM) et soumis à la loi générale genevoise sur le logement et la protection des locataires (LGL; rs/GE I 4 05). Ce même 17 février 2010, les parties ont signé l'avenant suivant: "L'immeuble sort du régi[m]e HLM au 31 décembre 2011. D'entente entre les parties, il est donc précisé ce qui suit: Dès le 1er mars 2012, le loyer passe à CHF 2'100.- + les charges, selon l'avis de majoration ci-joint. (...) " L'avis officiel de majoration de loyer annexé à cet avenant énonce que le loyer annuel sera augmenté à 25'200 fr. dès le 1 er mars 2012. Il est motivé ainsi: "Fin du régime HLM. CO 269a, lettre a: Adaptation aux loyers usuels pratiqués dans le quartier, soit une hausse de CHF 525.50/mois". Les locataires n'ont pas contesté cet avis dans le délai de trente jours imparti par l'art. 270 al. 1 CO. A.b. Par courrier du 29 novembre 2011, les locataires ont sollicité une baisse de loyer de 474 fr. 50 par mois dès le 1 er mars 2012. Ils ont demandé qu'un calcul de rendement soit effectué. Répondant par lettre du 6 décembre 2011, la bailleresse a refusé d'entrer en matière. B. B.a. Les locataires ont alors saisi l'autorité de conciliation le 23 décembre 2011, puis ont déposé une demande devant le Tribunal des baux et loyers du canton de Genève le 21 juin 2012. Ils ont conclu à ce que soit constatée la nullité de l'avis de majoration du 17 février 2010 et à ce que leur loyer soit fixé à 13'200 fr. par an dès le 1 er mars 2012. Par ordonnance du 10 août 2012, le Tribunal a invité la bailleresse à produire les pièces nécessaires au calcul du rendement net de l'immeuble concerné; il a attiré son attention sur les conséquences d'un refus de produire ces pièces, à savoir l'indice de ce qu'elle cachait un rendement abusif. Dans sa réponse du 28 octobre 2012, la bailleresse a conclu à l'irrecevabilité de la demande, subsidiairement à son rejet, et à ce que soit constatée l'entrée en force, au 1 er mars 2012, du loyer fixé selon l'avis de majoration du 17 février 2010. B.b. Par jugement du 22 novembre 2013, le Tribunal des baux a constaté la nullité de l'avis de majoration. Il a fixé le loyer annuel à 16'656 fr. (charges non comprises) dès le 1 er mars 2012 et a rejeté toutes autres conclusions. En substance, le Tribunal a jugé que la convention prévoyant une augmentation de loyer prédéterminée devait être qualifiée de bail à loyers échelonnés au sens de l'art. 269c CO. La clause d'échelonnement était viciée à plus d'un titre; en particulier, le bail avait été conclu pour une durée inférieure à trois ans. L'avis de majoration du 17 février 2010 était donc nul, et les locataires en droit de solliciter une diminution de loyer dès le 1 er mars 2012. Ils pouvaient demander un calcul de rendement étant donné que l'immeuble sortait du contrôle cantonal des loyers. A l'issue de ce calcul, le loyer annuel admissible se révélait être de 16'656 fr. B.c. La bailleresse a fait appel de cette décision. Par arrêt du 22 juin 2015, la Chambre des baux et loyers de la Cour de justice a annulé le jugement entrepris et, statuant à nouveau, a fixé le loyer annuel à 16'740 fr. dès le 1 er mars 2012, toutes autres conclusions étant rejetées. La Cour a confirmé l'analyse des premiers juges quant à la nullité de la clause d'échelonnement. Elle a en revanche admis un grief relatif aux charges d'exploitation et adapté en conséquence le calcul de rendement. C. La bailleresse saisit le Tribunal fédéral d'un recours en matière civile dans lequel elle requiert que l'avenant du 17 février 2010 soit déclaré valable et qu'il soit dit que le loyer annuel s'élève à 25'200 fr. dès le 1er mars 2012, respectivement que la requête en diminution de loyer soit déclarée irrecevable ou soit rejetée. Elle demande enfin que les locataires soient condamnés à lui verser "la totalité du moins-perçu de loyer (différence de loyer) " depuis le 1er mars 2012, dans un délai de trente jours dès l'entrée en force de l'arrêt rendu. Les locataires intimés, qui procèdent sans avocat, concluent au rejet du recours. L'autorité précédente se réfère à son arrêt.